summaryrefslogtreecommitdiffstats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/layout.rs8
-rw-r--r--compiler/rustc_abi/src/lib.rs53
-rw-r--r--compiler/rustc_arena/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/ast.rs15
-rw-r--r--compiler/rustc_ast/src/expand/mod.rs17
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs1
-rw-r--r--compiler/rustc_ast/src/token.rs1
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs15
-rw-r--r--compiler/rustc_ast/src/util/parser.rs4
-rw-r--r--compiler/rustc_ast/src/visit.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs23
-rw-r--r--compiler/rustc_ast_lowering/src/index.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs117
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs133
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs27
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs7
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs50
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/mod.rs4
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs7
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs5
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs28
-rw-r--r--compiler/rustc_borrowck/src/borrow_set.rs7
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/constraint_generation.rs8
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs112
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs41
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs165
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs14
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs27
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs40
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs8
-rw-r--r--compiler/rustc_borrowck/src/invalidation.rs15
-rw-r--r--compiler/rustc_borrowck/src/lib.rs58
-rw-r--r--compiler/rustc_borrowck/src/nll.rs2
-rw-r--r--compiler/rustc_borrowck/src/place_ext.rs6
-rw-r--r--compiler/rustc_borrowck/src/places_conflict.rs33
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs8
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs149
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs12
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs16
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs117
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs13
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl25
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/compile_error.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/clone.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs11
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/hash.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs90
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs129
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/trace_macros.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/.cirrus.yml8
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml6
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/main.yml60
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml4
-rw-r--r--compiler/rustc_codegen_cranelift/.vscode/settings.json3
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock136
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml21
-rw-r--r--compiler/rustc_codegen_cranelift/Readme.md39
-rw-r--r--compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml35
-rw-r--r--compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/Cargo.lock7
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/Cargo.toml13
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs30
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/bench.rs82
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_backend.rs11
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs113
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/main.rs (renamed from compiler/rustc_codegen_cranelift/build_system/mod.rs)125
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/path.rs1
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/prepare.rs208
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/rustc_info.rs24
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/tests.rs141
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/usage.txt34
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/utils.rs51
-rwxr-xr-xcompiler/rustc_codegen_cranelift/clean_all.sh2
-rw-r--r--compiler/rustc_codegen_cranelift/docs/usage.md2
-rw-r--r--compiler/rustc_codegen_cranelift/example/alloc_example.rs7
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core.rs15
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs18
-rw-r--r--compiler/rustc_codegen_cranelift/example/std_example.rs68
-rw-r--r--compiler/rustc_codegen_cranelift/patches/0023-coretests-Ignore-failing-tests.patch36
-rw-r--r--compiler/rustc_codegen_cranelift/patches/coretests-lock.toml35
-rw-r--r--compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml304
-rw-r--r--compiler/rustc_codegen_cranelift/patches/rand-lock.toml346
-rw-r--r--compiler/rustc_codegen_cranelift/patches/regex-lock.toml439
-rw-r--r--compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml (renamed from compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock)122
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain4
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs27
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs27
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/rustup.sh6
-rw-r--r--compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh55
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/allocator.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs26
-rw-r--r--compiler/rustc_codegen_cranelift/src/codegen_i128.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs26
-rw-r--r--compiler/rustc_codegen_cranelift/src/config.rs5
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs30
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs200
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs23
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs138
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs6
-rw-r--r--compiler/rustc_codegen_cranelift/src/num.rs18
-rw-r--r--compiler/rustc_codegen_cranelift/src/pretty_clif.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/trap.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/value_and_place.rs21
-rwxr-xr-xcompiler/rustc_codegen_cranelift/test.sh2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/y.rs33
-rwxr-xr-xcompiler/rustc_codegen_cranelift/y.sh6
-rw-r--r--compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml32
-rw-r--r--compiler/rustc_codegen_gcc/Cargo.lock4
-rw-r--r--compiler/rustc_codegen_gcc/Readme.md17
-rw-r--r--compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml1
-rwxr-xr-xcompiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh8
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core.rs24
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs3
-rw-r--r--compiler/rustc_codegen_gcc/example/std_example.rs1
-rw-r--r--compiler/rustc_codegen_gcc/failing-ui-tests.txt6
-rw-r--r--compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch49
-rw-r--r--compiler/rustc_codegen_gcc/rust-toolchain2
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs1
-rw-r--r--compiler/rustc_codegen_gcc/src/attributes.rs39
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs74
-rw-r--r--compiler/rustc_codegen_gcc/src/common.rs44
-rw-r--r--compiler/rustc_codegen_gcc/src/consts.rs24
-rw-r--r--compiler/rustc_codegen_gcc/src/context.rs15
-rw-r--r--compiler/rustc_codegen_gcc/src/coverageinfo.rs64
-rw-r--r--compiler/rustc_codegen_gcc/src/declare.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/archs.rs46
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs7
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs169
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/simd.rs18
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/type_of.rs11
-rwxr-xr-xcompiler/rustc_codegen_gcc/test.sh8
-rw-r--r--compiler/rustc_codegen_gcc/tools/generate_intrinsics.py3
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl8
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs62
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs45
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs53
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs19
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs44
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs (renamed from compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs)0
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs (renamed from compiler/rustc_codegen_ssa/src/coverageinfo/map.rs)1
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs82
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs13
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/utils.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs114
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs20
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs45
-rw-r--r--compiler/rustc_codegen_llvm/src/va_arg.rs2
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs289
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs173
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs386
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs44
-rw-r--r--compiler/rustc_codegen_ssa/src/common.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs62
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs64
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs41
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs23
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs65
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/locals.rs75
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs61
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs122
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs143
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/consts.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs58
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs22
-rw-r--r--compiler/rustc_const_eval/messages.ftl400
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs280
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs106
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs132
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs94
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs7
-rw-r--r--compiler/rustc_const_eval/src/errors.rs688
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs49
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs47
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs27
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs106
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs63
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs68
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs13
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs28
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs57
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs217
-rw-r--r--compiler/rustc_const_eval/src/lib.rs13
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs75
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs213
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs15
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/resolver.rs8
-rw-r--r--compiler/rustc_const_eval/src/transform/promote_consts.rs33
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs96
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs15
-rw-r--r--compiler/rustc_const_eval/src/util/mod.rs26
-rw-r--r--compiler/rustc_const_eval/src/util/type_name.rs1
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/intern.rs8
-rw-r--r--compiler/rustc_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/src/sharded.rs48
-rw-r--r--compiler/rustc_data_structures/src/small_str.rs68
-rw-r--r--compiler/rustc_data_structures/src/small_str/tests.rs20
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs64
-rw-r--r--compiler/rustc_data_structures/src/sync.rs11
-rw-r--r--compiler/rustc_data_structures/src/temp_dir.rs2
-rw-r--r--compiler/rustc_data_structures/src/unord.rs8
-rw-r--r--compiler/rustc_driver/Cargo.toml3
-rw-r--r--compiler/rustc_driver/src/lib.rs2
-rw-r--r--compiler/rustc_driver_impl/src/args.rs9
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs231
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs6
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0133.md19
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0741.md12
-rw-r--r--compiler/rustc_error_messages/src/lib.rs39
-rw-r--r--compiler/rustc_errors/Cargo.toml2
-rw-r--r--compiler/rustc_errors/messages.ftl6
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs28
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs40
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs23
-rw-r--r--compiler/rustc_errors/src/emitter.rs4
-rw-r--r--compiler/rustc_errors/src/lib.rs40
-rw-r--r--compiler/rustc_errors/src/markdown/mod.rs76
-rw-r--r--compiler/rustc_errors/src/markdown/parse.rs588
-rw-r--r--compiler/rustc_errors/src/markdown/term.rs189
-rw-r--r--compiler/rustc_errors/src/markdown/tests/input.md50
-rw-r--r--compiler/rustc_errors/src/markdown/tests/output.stdout35
-rw-r--r--compiler/rustc_errors/src/markdown/tests/parse.rs312
-rw-r--r--compiler/rustc_errors/src/markdown/tests/term.rs90
-rw-r--r--compiler/rustc_expand/src/base.rs15
-rw-r--r--compiler/rustc_expand/src/config.rs30
-rw-r--r--compiler/rustc_expand/src/expand.rs69
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs50
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs4
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs23
-rw-r--r--compiler/rustc_feature/src/active.rs10
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs16
-rw-r--r--compiler/rustc_feature/src/removed.rs4
-rw-r--r--compiler/rustc_fluent_macro/src/fluent.rs42
-rw-r--r--compiler/rustc_hir/src/hir.rs55
-rw-r--r--compiler/rustc_hir/src/hir_id.rs4
-rw-r--r--compiler/rustc_hir/src/intravisit.rs13
-rw-r--r--compiler/rustc_hir/src/lang_items.rs3
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl26
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs575
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs124
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs1296
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs408
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs22
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs53
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs272
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs183
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs62
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs278
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs61
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs102
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs59
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs57
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs328
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs73
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs89
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs131
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs63
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs81
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs34
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/explicit.rs61
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs50
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs33
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs12
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl8
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs133
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs34
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs31
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs34
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs246
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs125
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs50
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs310
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs87
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs41
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs133
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs253
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/generator_interior/mod.rs20
-rw-r--r--compiler/rustc_hir_typeck/src/inherited.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs60
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs55
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs60
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs54
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs74
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs23
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs119
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs28
-rw-r--r--compiler/rustc_incremental/src/assert_module_sources.rs9
-rw-r--r--compiler/rustc_incremental/src/lib.rs1
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs11
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs203
-rw-r--r--compiler/rustc_incremental/src/persist/fs/tests.rs27
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs8
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs12
-rw-r--r--compiler/rustc_incremental/src/persist/work_product.rs6
-rw-r--r--compiler/rustc_infer/messages.ftl3
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs24
-rw-r--r--compiler/rustc_infer/src/infer/at.rs5
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs13
-rw-r--r--compiler/rustc_infer/src/infer/canonical/mod.rs8
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs33
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs63
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs49
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs54
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs27
-rw-r--r--compiler/rustc_infer/src/infer/freshen.rs8
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs10
-rw-r--r--compiler/rustc_infer/src/infer/higher_ranked/mod.rs33
-rw-r--r--compiler/rustc_infer/src/infer/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs190
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs138
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/table.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs29
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs5
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs12
-rw-r--r--compiler/rustc_infer/src/infer/projection.rs36
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/leak_check.rs144
-rw-r--r--compiler/rustc_infer/src/infer/region_constraints/mod.rs19
-rw-r--r--compiler/rustc_infer/src/infer/sub.rs4
-rw-r--r--compiler/rustc_infer/src/infer/type_variable.rs3
-rw-r--r--compiler/rustc_infer/src/infer/undo_log.rs15
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs8
-rw-r--r--compiler/rustc_infer/src/traits/mod.rs11
-rw-r--r--compiler/rustc_infer/src/traits/util.rs128
-rw-r--r--compiler/rustc_interface/Cargo.toml1
-rw-r--r--compiler/rustc_interface/messages.ftl1
-rw-r--r--compiler/rustc_interface/src/errors.rs4
-rw-r--r--compiler/rustc_interface/src/interface.rs49
-rw-r--r--compiler/rustc_interface/src/passes.rs166
-rw-r--r--compiler/rustc_interface/src/queries.rs44
-rw-r--r--compiler/rustc_interface/src/tests.rs122
-rw-r--r--compiler/rustc_interface/src/util.rs76
-rw-r--r--compiler/rustc_lint/messages.ftl22
-rw-r--r--compiler/rustc_lint/src/builtin.rs104
-rw-r--r--compiler/rustc_lint/src/context.rs6
-rw-r--r--compiler/rustc_lint/src/drop_forget_useless.rs44
-rw-r--r--compiler/rustc_lint/src/errors.rs2
-rw-r--r--compiler/rustc_lint/src/invalid_from_utf8.rs118
-rw-r--r--compiler/rustc_lint/src/late.rs11
-rw-r--r--compiler/rustc_lint/src/lib.rs6
-rw-r--r--compiler/rustc_lint/src/lints.rs151
-rw-r--r--compiler/rustc_lint/src/multiple_supertrait_upcastable.rs2
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs4
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs9
-rw-r--r--compiler/rustc_lint/src/reference_casting.rs73
-rw-r--r--compiler/rustc_lint/src/traits.rs5
-rw-r--r--compiler/rustc_lint/src/types.rs407
-rw-r--r--compiler/rustc_lint/src/unused.rs158
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs205
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs10
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h1
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp118
-rw-r--r--compiler/rustc_log/Cargo.toml2
-rw-r--r--compiler/rustc_macros/Cargo.toml3
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs61
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs16
-rw-r--r--compiler/rustc_macros/src/diagnostics/error.rs10
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs25
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs29
-rw-r--r--compiler/rustc_metadata/messages.ftl6
-rw-r--r--compiler/rustc_metadata/src/creader.rs20
-rw-r--r--compiler/rustc_metadata/src/errors.rs11
-rw-r--r--compiler/rustc_metadata/src/fs.rs43
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/locator.rs17
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs71
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs18
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs524
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs41
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs2
-rw-r--r--compiler/rustc_middle/Cargo.toml1
-rw-r--r--compiler/rustc_middle/messages.ftl35
-rw-r--r--compiler/rustc_middle/src/arena.rs10
-rw-r--r--compiler/rustc_middle/src/error.rs55
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs19
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs59
-rw-r--r--compiler/rustc_middle/src/infer/unify_key.rs7
-rw-r--r--compiler/rustc_middle/src/lib.rs5
-rw-r--r--compiler/rustc_middle/src/lint.rs9
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs11
-rw-r--r--compiler/rustc_middle/src/middle/privacy.rs35
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs12
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs13
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs48
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs444
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs31
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs23
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs12
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs230
-rw-r--r--compiler/rustc_middle/src/mir/mono.rs22
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs123
-rw-r--r--compiler/rustc_middle/src/mir/query.rs10
-rw-r--r--compiler/rustc_middle/src/mir/spanview.rs9
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs100
-rw-r--r--compiler/rustc_middle/src/mir/tcx.rs47
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs5
-rw-r--r--compiler/rustc_middle/src/mir/traversal.rs77
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs16
-rw-r--r--compiler/rustc_middle/src/query/erase.rs21
-rw-r--r--compiler/rustc_middle/src/query/keys.rs15
-rw-r--r--compiler/rustc_middle/src/query/mod.rs100
-rw-r--r--compiler/rustc_middle/src/query/on_disk_cache.rs7
-rw-r--r--compiler/rustc_middle/src/thir.rs13
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs3
-rw-r--r--compiler/rustc_middle/src/traits/chalk.rs396
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs192
-rw-r--r--compiler/rustc_middle/src/traits/query.rs2
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs26
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs83
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs131
-rw-r--r--compiler/rustc_middle/src/traits/specialization_graph.rs2
-rw-r--r--compiler/rustc_middle/src/traits/structural_impls.rs96
-rw-r--r--compiler/rustc_middle/src/traits/util.rs4
-rw-r--r--compiler/rustc_middle/src/ty/_match.rs2
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs6
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs2
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs2
-rw-r--r--compiler/rustc_middle/src/ty/codec.rs35
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs285
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs9
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs204
-rw-r--r--compiler/rustc_middle/src/ty/context.rs622
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs65
-rw-r--r--compiler/rustc_middle/src/ty/error.rs14
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs22
-rw-r--r--compiler/rustc_middle/src/ty/fold.rs29
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs26
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs19
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs12
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs200
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs333
-rw-r--r--compiler/rustc_middle/src/ty/opaque_types.rs12
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs189
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs83
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs142
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs573
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs61
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs10
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs6
-rw-r--r--compiler/rustc_middle/src/ty/util.rs150
-rw-r--r--compiler/rustc_middle/src/ty/vtable.rs2
-rw-r--r--compiler/rustc_middle/src/values.rs15
-rw-r--r--compiler/rustc_mir_build/messages.ftl3
-rw-r--r--compiler/rustc_mir_build/src/build/custom/mod.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs24
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs19
-rw-r--r--compiler/rustc_mir_build/src/build/expr/category.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs13
-rw-r--r--compiler/rustc_mir_build/src/build/expr/stmt.rs7
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs24
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs23
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs3
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs9
-rw-r--r--compiler/rustc_mir_build/src/errors.rs4
-rw-r--r--compiler/rustc_mir_build/src/lib.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs20
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs101
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/mod.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs12
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs380
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs33
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs127
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs117
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/cursor.rs112
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/direction.rs70
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs114
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/graphviz.rs98
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/mod.rs61
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/tests.rs17
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/visitor.rs45
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs7
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs18
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/mod.rs28
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs87
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs8
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs154
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/mod.rs74
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs25
-rw-r--r--compiler/rustc_mir_dataflow/src/un_derefer.rs22
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs8
-rw-r--r--compiler/rustc_mir_transform/messages.ftl2
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs1
-rw-r--r--compiler/rustc_mir_transform/src/check_alignment.rs7
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs37
-rw-r--r--compiler/rustc_mir_transform/src/const_goto.rs4
-rw-r--r--compiler/rustc_mir_transform/src/const_prop.rs54
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coverage/debug.rs17
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs7
-rw-r--r--compiler/rustc_mir_transform/src/coverage/tests.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs60
-rw-r--r--compiler/rustc_mir_transform/src/deduce_param_attrs.rs7
-rw-r--r--compiler/rustc_mir_transform/src/deref_separator.rs10
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs19
-rw-r--r--compiler/rustc_mir_transform/src/dump_mir.rs15
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs2
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs25
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs36
-rw-r--r--compiler/rustc_mir_transform/src/function_item_references.rs12
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs75
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs40
-rw-r--r--compiler/rustc_mir_transform/src/inline/cycle.rs2
-rw-r--r--compiler/rustc_mir_transform/src/large_enums.rs16
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs17
-rw-r--r--compiler/rustc_mir_transform/src/lower_intrinsics.rs61
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs9
-rw-r--r--compiler/rustc_mir_transform/src/normalize_array_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/prettify.rs150
-rw-r--r--compiler/rustc_mir_transform/src/remove_uninit_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/required_consts.rs4
-rw-r--r--compiler/rustc_mir_transform/src/separate_const_switch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs64
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs62
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs8
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs248
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs5
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs1274
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/default.rs644
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/mod.rs673
-rw-r--r--compiler/rustc_monomorphize/src/util.rs4
-rw-r--r--compiler/rustc_parse/messages.ftl3
-rw-r--r--compiler/rustc_parse/src/errors.rs11
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs2
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs17
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs10
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs41
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs151
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs44
-rw-r--r--compiler/rustc_parse/src/parser/item.rs6
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs57
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs7
-rw-r--r--compiler/rustc_passes/messages.ftl5
-rw-r--r--compiler/rustc_passes/src/check_attr.rs62
-rw-r--r--compiler/rustc_passes/src/check_const.rs5
-rw-r--r--compiler/rustc_passes/src/dead.rs11
-rw-r--r--compiler/rustc_passes/src/errors.rs19
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs7
-rw-r--r--compiler/rustc_passes/src/layout_test.rs10
-rw-r--r--compiler/rustc_passes/src/liveness.rs7
-rw-r--r--compiler/rustc_passes/src/loops.rs10
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs1
-rw-r--r--compiler/rustc_privacy/messages.ftl7
-rw-r--r--compiler/rustc_privacy/src/errors.rs29
-rw-r--r--compiler/rustc_privacy/src/lib.rs479
-rw-r--r--compiler/rustc_query_impl/Cargo.toml4
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs16
-rw-r--r--compiler/rustc_query_system/src/dep_graph/dep_node.rs13
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs11
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs31
-rw-r--r--compiler/rustc_query_system/src/values.rs7
-rw-r--r--compiler/rustc_resolve/messages.ftl56
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs33
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs269
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs20
-rw-r--r--compiler/rustc_resolve/src/errors.rs145
-rw-r--r--compiler/rustc_resolve/src/ident.rs274
-rw-r--r--compiler/rustc_resolve/src/imports.rs382
-rw-r--r--compiler/rustc_resolve/src/late.rs55
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs227
-rw-r--r--compiler/rustc_resolve/src/lib.rs240
-rw-r--r--compiler/rustc_resolve/src/macros.rs41
-rw-r--r--compiler/rustc_serialize/Cargo.toml2
-rw-r--r--compiler/rustc_session/Cargo.toml2
-rw-r--r--compiler/rustc_session/messages.ftl4
-rw-r--r--compiler/rustc_session/src/code_stats.rs60
-rw-r--r--compiler/rustc_session/src/config.rs682
-rw-r--r--compiler/rustc_session/src/errors.rs8
-rw-r--r--compiler/rustc_session/src/lib.rs2
-rw-r--r--compiler/rustc_session/src/options.rs94
-rw-r--r--compiler/rustc_session/src/output.rs43
-rw-r--r--compiler/rustc_session/src/parse.rs12
-rw-r--r--compiler/rustc_session/src/search_paths.rs6
-rw-r--r--compiler/rustc_session/src/session.rs157
-rw-r--r--compiler/rustc_smir/Cargo.toml1
-rw-r--r--compiler/rustc_smir/src/lib.rs3
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs420
-rw-r--r--compiler/rustc_smir/src/stable_mir/mir/body.rs22
-rw-r--r--compiler/rustc_smir/src/stable_mir/mod.rs28
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs36
-rw-r--r--compiler/rustc_span/Cargo.toml2
-rw-r--r--compiler/rustc_span/src/hygiene.rs17
-rw-r--r--compiler/rustc_span/src/lib.rs31
-rw-r--r--compiler/rustc_span/src/source_map.rs21
-rw-r--r--compiler/rustc_span/src/symbol.rs18
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs2
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs151
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs8
-rw-r--r--compiler/rustc_target/Cargo.toml5
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs11
-rw-r--r--compiler/rustc_target/src/abi/call/x86_64.rs12
-rw-r--r--compiler/rustc_target/src/lib.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_tvos.rs4
-rw-r--r--compiler/rustc_target/src/spec/aarch64_be_unknown_netbsd.rs17
-rw-r--r--compiler/rustc_target/src/spec/apple/tests.rs5
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs18
-rw-r--r--compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs15
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs337
-rw-r--r--compiler/rustc_target/src/spec/loongarch64_unknown_none.rs22
-rw-r--r--compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs23
-rw-r--r--compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs2
-rw-r--r--compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs2
-rw-r--r--compiler/rustc_target/src/spec/mod.rs273
-rw-r--r--compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs31
-rw-r--r--compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs19
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_ios.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_tvos.rs9
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_linux_android.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_solaris.rs3
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_sun_solaris.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs3
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_l4re_uclibc.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_redox.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs1
-rw-r--r--compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs1
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs11
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs195
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs153
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs20
-rw-r--r--compiler/rustc_trait_selection/src/solve/canonicalize.rs31
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs392
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs30
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs67
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs307
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs147
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs435
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs166
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs220
-rw-r--r--compiler/rustc_trait_selection/src/solve/opaques.rs23
-rw-r--r--compiler/rustc_trait_selection/src/solve/project_goals.rs269
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs86
-rw-r--r--compiler/rustc_trait_selection/src/solve/weak_types.rs19
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs40
-rw-r--r--compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs151
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs306
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs71
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs518
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs22
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs259
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs80
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs120
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs176
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs314
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs32
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs46
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs62
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs253
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs214
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs70
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs111
-rw-r--r--compiler/rustc_traits/Cargo.toml3
-rw-r--r--compiler/rustc_traits/src/chalk/db.rs792
-rw-r--r--compiler/rustc_traits/src/chalk/lowering.rs1241
-rw-r--r--compiler/rustc_traits/src/chalk/mod.rs169
-rw-r--r--compiler/rustc_traits/src/codegen.rs19
-rw-r--r--compiler/rustc_traits/src/evaluate_obligation.rs11
-rw-r--r--compiler/rustc_traits/src/lib.rs6
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs17
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs34
-rw-r--r--compiler/rustc_traits/src/type_op.rs24
-rw-r--r--compiler/rustc_transmute/src/layout/mod.rs42
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs23
-rw-r--r--compiler/rustc_transmute/src/lib.rs36
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/mod.rs330
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/tests.rs4
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml1
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs36
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs29
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs45
-rw-r--r--compiler/rustc_ty_utils/src/errors.rs2
-rw-r--r--compiler/rustc_ty_utils/src/implied_bounds.rs76
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs147
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs208
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs2
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs298
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs165
-rw-r--r--compiler/rustc_type_ir/src/lib.rs33
-rw-r--r--compiler/rustc_type_ir/src/structural_impls.rs39
-rw-r--r--compiler/rustc_type_ir/src/sty.rs298
812 files changed, 30987 insertions, 19891 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 73f9deb31..f6875d895 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -134,7 +134,7 @@ pub trait LayoutCalculator {
scalar_valid_range: (Bound<u128>, Bound<u128>),
discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
discriminants: impl Iterator<Item = (VariantIdx, i128)>,
- niche_optimize_enum: bool,
+ dont_niche_optimize_enum: bool,
always_sized: bool,
) -> Option<LayoutS> {
let dl = self.current_data_layout();
@@ -183,10 +183,10 @@ pub trait LayoutCalculator {
// (Typechecking will reject discriminant-sizing attrs.)
let v = present_first;
- let kind = if is_enum || variants[v].is_empty() {
+ let kind = if is_enum || variants[v].is_empty() || always_sized {
StructKind::AlwaysSized
} else {
- if !always_sized { StructKind::MaybeUnsized } else { StructKind::AlwaysSized }
+ StructKind::MaybeUnsized
};
let mut st = self.univariant(dl, &variants[v], repr, kind)?;
@@ -280,7 +280,7 @@ pub trait LayoutCalculator {
}
let calculate_niche_filling_layout = || -> Option<TmpLayout> {
- if niche_optimize_enum {
+ if dont_niche_optimize_enum {
return None;
}
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 43db66a3c..e1b9987f5 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -209,7 +209,7 @@ pub enum TargetDataLayoutErrors<'a> {
InvalidAddressSpace { addr_space: &'a str, cause: &'a str, err: ParseIntError },
InvalidBits { kind: &'a str, bit: &'a str, cause: &'a str, err: ParseIntError },
MissingAlignment { cause: &'a str },
- InvalidAlignment { cause: &'a str, err: String },
+ InvalidAlignment { cause: &'a str, err: AlignFromBytesError },
InconsistentTargetArchitecture { dl: &'a str, target: &'a str },
InconsistentTargetPointerWidth { pointer_size: u64, target: u32 },
InvalidBitsSize { err: String },
@@ -414,7 +414,9 @@ pub struct Size {
// Safety: Ord is implement as just comparing numerical values and numerical values
// are not changed by (de-)serialization.
#[cfg(feature = "nightly")]
-unsafe impl StableOrd for Size {}
+unsafe impl StableOrd for Size {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
// This is debug-printed a lot in larger structs, don't waste too much space there
impl fmt::Debug for Size {
@@ -640,30 +642,65 @@ impl fmt::Debug for Align {
}
}
+#[derive(Clone, Copy)]
+pub enum AlignFromBytesError {
+ NotPowerOfTwo(u64),
+ TooLarge(u64),
+}
+
+impl AlignFromBytesError {
+ pub fn diag_ident(self) -> &'static str {
+ match self {
+ Self::NotPowerOfTwo(_) => "not_power_of_two",
+ Self::TooLarge(_) => "too_large",
+ }
+ }
+
+ pub fn align(self) -> u64 {
+ let (Self::NotPowerOfTwo(align) | Self::TooLarge(align)) = self;
+ align
+ }
+}
+
+impl fmt::Debug for AlignFromBytesError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+}
+
+impl fmt::Display for AlignFromBytesError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ AlignFromBytesError::NotPowerOfTwo(align) => write!(f, "`{align}` is not a power of 2"),
+ AlignFromBytesError::TooLarge(align) => write!(f, "`{align}` is too large"),
+ }
+ }
+}
+
impl Align {
pub const ONE: Align = Align { pow2: 0 };
pub const MAX: Align = Align { pow2: 29 };
#[inline]
- pub fn from_bits(bits: u64) -> Result<Align, String> {
+ pub fn from_bits(bits: u64) -> Result<Align, AlignFromBytesError> {
Align::from_bytes(Size::from_bits(bits).bytes())
}
#[inline]
- pub fn from_bytes(align: u64) -> Result<Align, String> {
+ pub fn from_bytes(align: u64) -> Result<Align, AlignFromBytesError> {
// Treat an alignment of 0 bytes like 1-byte alignment.
if align == 0 {
return Ok(Align::ONE);
}
#[cold]
- fn not_power_of_2(align: u64) -> String {
- format!("`{}` is not a power of 2", align)
+ fn not_power_of_2(align: u64) -> AlignFromBytesError {
+ AlignFromBytesError::NotPowerOfTwo(align)
}
#[cold]
- fn too_large(align: u64) -> String {
- format!("`{}` is too large", align)
+ fn too_large(align: u64) -> AlignFromBytesError {
+ AlignFromBytesError::TooLarge(align)
}
let tz = align.trailing_zeros();
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 6e15f06a7..ba47ebd68 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -67,7 +67,7 @@ struct ArenaChunk<T = u8> {
unsafe impl<#[may_dangle] T> Drop for ArenaChunk<T> {
fn drop(&mut self) {
- unsafe { Box::from_raw(self.storage.as_mut()) };
+ unsafe { drop(Box::from_raw(self.storage.as_mut())) }
}
}
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4360fbeb9..a7198fbf8 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1295,6 +1295,7 @@ impl Expr {
ExprKind::Yield(..) => ExprPrecedence::Yield,
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
+ ExprKind::Become(..) => ExprPrecedence::Become,
ExprKind::Err => ExprPrecedence::Err,
}
}
@@ -1515,6 +1516,11 @@ pub enum ExprKind {
/// with an optional value to be returned.
Yeet(Option<P<Expr>>),
+ /// A tail call return, with the value to be returned.
+ ///
+ /// While `.0` must be a function call, we check this later, after parsing.
+ Become(P<Expr>),
+
/// Bytes included via `include_bytes!`
/// Added for optimization purposes to avoid the need to escape
/// large binary blobs - should always behave like [`ExprKind::Lit`]
@@ -2646,6 +2652,15 @@ pub struct NormalAttr {
pub tokens: Option<LazyAttrTokenStream>,
}
+impl NormalAttr {
+ pub fn from_ident(ident: Ident) -> Self {
+ Self {
+ item: AttrItem { path: Path::from_ident(ident), args: AttrArgs::Empty, tokens: None },
+ tokens: None,
+ }
+ }
+}
+
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
pub struct AttrItem {
pub path: Path,
diff --git a/compiler/rustc_ast/src/expand/mod.rs b/compiler/rustc_ast/src/expand/mod.rs
index 2ee1bfe0a..942347383 100644
--- a/compiler/rustc_ast/src/expand/mod.rs
+++ b/compiler/rustc_ast/src/expand/mod.rs
@@ -1,3 +1,20 @@
//! Definitions shared by macros / syntax extensions and e.g. `rustc_middle`.
+use rustc_span::{def_id::DefId, symbol::Ident};
+
+use crate::MetaItem;
+
pub mod allocator;
+
+#[derive(Debug, Clone, Encodable, Decodable, HashStable_Generic)]
+pub struct StrippedCfgItem<ModId = DefId> {
+ pub parent_module: ModId,
+ pub name: Ident,
+ pub cfg: MetaItem,
+}
+
+impl<ModId> StrippedCfgItem<ModId> {
+ pub fn map_mod_id<New>(self, f: impl FnOnce(ModId) -> New) -> StrippedCfgItem<New> {
+ StrippedCfgItem { parent_module: f(self.parent_module), name: self.name, cfg: self.cfg }
+ }
+}
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 66b94d12a..53a9c9a04 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1457,6 +1457,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
ExprKind::Yeet(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
+ ExprKind::Become(expr) => vis.visit_expr(expr),
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
ExprKind::OffsetOf(container, fields) => {
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index 7ef39f802..6646fa944 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -11,6 +11,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc;
use rustc_macros::HashStable_Generic;
use rustc_span::symbol::{kw, sym};
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{self, edition::Edition, Span, DUMMY_SP};
use std::borrow::Cow;
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index db296aa44..ca4a739ab 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -25,7 +25,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
-use std::{fmt, iter};
+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
@@ -410,8 +410,17 @@ impl TokenStream {
t1.next().is_none() && t2.next().is_none()
}
- pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
- TokenStream(Lrc::new(self.0.iter().enumerate().map(|(i, tree)| f(i, tree)).collect()))
+ /// Applies the supplied function to each `TokenTree` and its index in `self`, returning a new `TokenStream`
+ ///
+ /// It is equivalent to `TokenStream::new(self.trees().cloned().enumerate().map(|(i, tt)| f(i, tt)).collect())`.
+ pub fn map_enumerated_owned(
+ mut self,
+ mut f: impl FnMut(usize, TokenTree) -> TokenTree,
+ ) -> TokenStream {
+ let owned = Lrc::make_mut(&mut self.0); // clone if necessary
+ // rely on vec's in-place optimizations to avoid another allocation
+ *owned = mem::take(owned).into_iter().enumerate().map(|(i, tree)| f(i, tree)).collect();
+ self
}
/// Create a token stream containing a single token with alone spacing.
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 35afd5423..096077e09 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -245,6 +245,7 @@ pub enum ExprPrecedence {
Ret,
Yield,
Yeet,
+ Become,
Range,
@@ -298,7 +299,8 @@ impl ExprPrecedence {
| ExprPrecedence::Continue
| ExprPrecedence::Ret
| ExprPrecedence::Yield
- | ExprPrecedence::Yeet => PREC_JUMP,
+ | ExprPrecedence::Yeet
+ | ExprPrecedence::Become => PREC_JUMP,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 275692ad5..d9de5b8e1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -908,6 +908,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Yeet(optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
+ ExprKind::Become(expr) => visitor.visit_expr(expr),
ExprKind::MacCall(mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 941d31795..d350498bc 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -44,6 +44,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| asm::InlineAsmArch::AArch64
| asm::InlineAsmArch::RiscV32
| asm::InlineAsmArch::RiscV64
+ | asm::InlineAsmArch::LoongArch64
);
if !is_stable && !self.tcx.features().asm_experimental_arch {
feature_err(
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 5e0ab80c6..dcaaaafed 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -71,9 +71,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
let kind = match &e.kind {
ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
- ExprKind::ConstBlock(anon_const) => {
- let anon_const = self.lower_anon_const(anon_const);
- hir::ExprKind::ConstBlock(anon_const)
+ ExprKind::ConstBlock(c) => {
+ let c = self.with_new_scopes(|this| hir::ConstBlock {
+ def_id: this.local_def_id(c.id),
+ hir_id: this.lower_node_id(c.id),
+ body: this.lower_const_body(c.value.span, Some(&c.value)),
+ });
+ hir::ExprKind::ConstBlock(c)
}
ExprKind::Repeat(expr, count) => {
let expr = self.lower_expr(expr);
@@ -271,6 +275,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Ret(e)
}
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
+ ExprKind::Become(sub_expr) => {
+ let sub_expr = self.lower_expr(sub_expr);
+ hir::ExprKind::Become(sub_expr)
+ }
ExprKind::InlineAsm(asm) => {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
@@ -665,14 +673,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(
inner_hir_id,
&[Attribute {
- kind: AttrKind::Normal(ptr::P(NormalAttr {
- item: AttrItem {
- path: Path::from_ident(Ident::new(sym::track_caller, span)),
- args: AttrArgs::Empty,
- tokens: None,
- },
- tokens: None,
- })),
+ kind: AttrKind::Normal(ptr::P(NormalAttr::from_ident(Ident::new(sym::track_caller, span)))),
id: self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id(),
style: AttrStyle::Outer,
span: unstable_span,
diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs
index 2e66c81eb..ce847906f 100644
--- a/compiler/rustc_ast_lowering/src/index.rs
+++ b/compiler/rustc_ast_lowering/src/index.rs
@@ -223,6 +223,14 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
});
}
+ fn visit_inline_const(&mut self, constant: &'hir ConstBlock) {
+ self.insert(DUMMY_SP, constant.hir_id, Node::ConstBlock(constant));
+
+ self.with_parent(constant.hir_id, |this| {
+ intravisit::walk_inline_const(this, constant);
+ });
+ }
+
fn visit_expr(&mut self, expr: &'hir Expr<'hir>) {
self.insert(expr.span, expr.hir_id, Node::Expr(expr));
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 08ee3761b..ab68436c0 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -3,6 +3,7 @@ use super::ResolverAstLoweringExt;
use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
use super::{FnDeclKind, LoweringContext, ParamMode};
+use hir::definitions::DefPathData;
use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
@@ -257,10 +258,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let itctx = ImplTraitContext::Universal;
- let (generics, decl) = this.lower_generics(generics, id, &itctx, |this| {
- let ret_id = asyncness.opt_return_id();
- this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
- });
+ let (generics, decl) =
+ this.lower_generics(generics, header.constness, id, &itctx, |this| {
+ let ret_id = asyncness.opt_return_id();
+ this.lower_fn_decl(&decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
+ });
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(*header),
@@ -295,6 +297,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, true);
let (generics, ty) = self.lower_generics(
&generics,
+ Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
@@ -316,6 +319,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Enum(enum_definition, generics) => {
let (generics, variants) = self.lower_generics(
generics,
+ Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@@ -329,6 +333,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Struct(struct_def, generics) => {
let (generics, struct_def) = self.lower_generics(
generics,
+ Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, struct_def),
@@ -338,6 +343,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Union(vdata, generics) => {
let (generics, vdata) = self.lower_generics(
generics,
+ Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| this.lower_variant_data(hir_id, vdata),
@@ -369,7 +375,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// parent lifetime.
let itctx = ImplTraitContext::Universal;
let (generics, (trait_ref, lowered_ty)) =
- self.lower_generics(ast_generics, id, &itctx, |this| {
+ self.lower_generics(ast_generics, *constness, id, &itctx, |this| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
trait_ref,
@@ -410,8 +416,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
}))
}
ItemKind::Trait(box Trait { is_auto, unsafety, generics, bounds, items }) => {
+ // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible
+ let constness = attrs
+ .unwrap_or(&[])
+ .iter()
+ .find(|x| x.has_name(sym::const_trait))
+ .map_or(Const::No, |x| Const::Yes(x.span));
let (generics, (unsafety, items, bounds)) = self.lower_generics(
generics,
+ constness,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@@ -431,6 +444,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::TraitAlias(generics, bounds) => {
let (generics, bounds) = self.lower_generics(
generics,
+ Const::No,
id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@@ -593,7 +607,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let fdec = &sig.decl;
let itctx = ImplTraitContext::Universal;
let (generics, (fn_dec, fn_args)) =
- self.lower_generics(generics, i.id, &itctx, |this| {
+ self.lower_generics(generics, Const::No, i.id, &itctx, |this| {
(
// Disallow `impl Trait` in foreign items.
this.lower_fn_decl(
@@ -745,6 +759,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
let (generics, kind) = self.lower_generics(
&generics,
+ Const::No,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| {
@@ -843,6 +858,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
add_ty_alias_where_clause(&mut generics, *where_clauses, false);
self.lower_generics(
&generics,
+ Const::No,
i.id,
&ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
|this| match ty {
@@ -1201,9 +1217,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
let itctx = ImplTraitContext::Universal;
- let (generics, decl) = self.lower_generics(generics, id, &itctx, |this| {
- this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
- });
+ let (generics, decl) =
+ self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
+ this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
+ });
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}
@@ -1275,6 +1292,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn lower_generics<T>(
&mut self,
generics: &Generics,
+ constness: Const,
parent_node_id: NodeId,
itctx: &ImplTraitContext,
f: impl FnOnce(&mut Self) -> T,
@@ -1372,6 +1390,87 @@ impl<'hir> LoweringContext<'_, 'hir> {
let impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds);
predicates.extend(impl_trait_bounds.into_iter());
+ // Desugar `~const` bound in generics into an additional `const host: bool` param
+ // if the effects feature is enabled.
+ if let Const::Yes(span) = constness && self.tcx.features().effects
+ // Do not add host param if it already has it (manually specified)
+ && !params.iter().any(|x| {
+ self.attrs.get(&x.hir_id.local_id).map_or(false, |attrs| {
+ attrs.iter().any(|x| x.has_name(sym::rustc_host))
+ })
+ })
+ {
+ let param_node_id = self.next_node_id();
+ let const_node_id = self.next_node_id();
+ let def_id = self.create_def(self.local_def_id(parent_node_id), param_node_id, DefPathData::TypeNs(sym::host), span);
+ let anon_const: LocalDefId = self.create_def(def_id, const_node_id, DefPathData::AnonConst, span);
+
+ let hir_id = self.next_id();
+ let const_id = self.next_id();
+ let const_expr_id = self.next_id();
+ let bool_id = self.next_id();
+
+ self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
+ self.children.push((anon_const, hir::MaybeOwner::NonOwner(const_id)));
+
+ let attr_id = self.tcx.sess.parse_sess.attr_id_generator.mk_attr_id();
+
+ let attrs = self.arena.alloc_from_iter([
+ Attribute {
+ kind: AttrKind::Normal(P(NormalAttr::from_ident(Ident::new(sym::rustc_host, span)))),
+ span,
+ id: attr_id,
+ style: AttrStyle::Outer,
+ },
+ ]);
+ self.attrs.insert(hir_id.local_id, attrs);
+
+ let const_body = self.lower_body(|this| {
+ (
+ &[],
+ hir::Expr {
+ hir_id: const_expr_id,
+ kind: hir::ExprKind::Lit(
+ this.arena.alloc(hir::Lit { node: LitKind::Bool(true), span }),
+ ),
+ span,
+ },
+ )
+ });
+
+ let param = hir::GenericParam {
+ def_id,
+ hir_id,
+ name: hir::ParamName::Plain(Ident { name: sym::host, span }),
+ span,
+ kind: hir::GenericParamKind::Const {
+ ty: self.arena.alloc(self.ty(
+ span,
+ hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ res: Res::PrimTy(hir::PrimTy::Bool),
+ span,
+ segments: self.arena.alloc_from_iter([hir::PathSegment {
+ ident: Ident { name: sym::bool, span },
+ hir_id: bool_id,
+ res: Res::PrimTy(hir::PrimTy::Bool),
+ args: None,
+ infer_args: false,
+ }]),
+ }),
+ )),
+ )),
+ default: Some(hir::AnonConst { def_id: anon_const, hir_id: const_id, body: const_body }),
+ },
+ colon_span: None,
+ pure_wrt_drop: false,
+ source: hir::GenericParamSource::Generics,
+ };
+
+ params.push(param);
+ }
+
let lowered_generics = self.arena.alloc(hir::Generics {
params: self.arena.alloc_from_iter(params),
predicates: self.arena.alloc_from_iter(predicates),
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 8d4f96639..429e62c4a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1539,9 +1539,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
);
debug!(?opaque_ty_def_id);
- // Contains the new lifetime definitions created for the TAIT (if any).
- let mut collected_lifetimes = Vec::new();
-
// If this came from a TAIT (as opposed to a function that returns an RPIT), we only want
// to capture the lifetimes that appear in the bounds. So visit the bounds to find out
// exactly which ones those are.
@@ -1558,20 +1555,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
debug!(?lifetimes_to_remap);
- self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
- let mut new_remapping = FxHashMap::default();
+ let mut new_remapping = FxHashMap::default();
- // If this opaque type is only capturing a subset of the lifetimes (those that appear
- // in bounds), then create the new lifetime parameters required and create a mapping
- // from the old `'a` (on the function) to the new `'a` (on the opaque type).
- collected_lifetimes = lctx.create_lifetime_defs(
- opaque_ty_def_id,
- &lifetimes_to_remap,
- &mut new_remapping,
- );
- debug!(?collected_lifetimes);
- debug!(?new_remapping);
+ // Contains the new lifetime definitions created for the TAIT (if any).
+ // If this opaque type is only capturing a subset of the lifetimes (those that appear in
+ // bounds), then create the new lifetime parameters required and create a mapping from the
+ // old `'a` (on the function) to the new `'a` (on the opaque type).
+ let collected_lifetimes =
+ self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping);
+ debug!(?collected_lifetimes);
+ debug!(?new_remapping);
+
+ // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
+ // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
+ let collected_lifetime_mapping: Vec<_> = collected_lifetimes
+ .iter()
+ .map(|(node_id, lifetime)| {
+ let id = self.next_node_id();
+ let lifetime = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
+ let def_id = self.local_def_id(*node_id);
+ (lifetime, def_id)
+ })
+ .collect();
+ debug!(?collected_lifetime_mapping);
+ self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
// Install the remapping from old to new (if any):
lctx.with_remapping(new_remapping, |lctx| {
// This creates HIR lifetime definitions as `hir::GenericParam`, in the given
@@ -1610,6 +1618,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let hir_bounds = lctx.lower_param_bounds(bounds, itctx);
debug!(?hir_bounds);
+ let lifetime_mapping = if in_trait {
+ self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ )
+ } else {
+ &mut []
+ };
+
let opaque_ty_item = hir::OpaqueTy {
generics: self.arena.alloc(hir::Generics {
params: lifetime_defs,
@@ -1620,6 +1638,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
bounds: hir_bounds,
origin,
+ lifetime_mapping,
in_trait,
};
debug!(?opaque_ty_item);
@@ -1628,20 +1647,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
})
});
- // This creates HIR lifetime arguments as `hir::GenericArg`, in the given example `type
- // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing `&['x]`.
- let lifetimes =
- self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(_, lifetime)| {
- let id = self.next_node_id();
- let l = self.new_named_lifetime(lifetime.id, id, lifetime.ident);
- hir::GenericArg::Lifetime(l)
- }));
- debug!(?lifetimes);
-
// `impl Trait` now just becomes `Foo<'a, 'b, ..>`.
hir::TyKind::OpaqueDef(
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
- lifetimes,
+ self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
+ ),
in_trait,
)
}
@@ -1655,7 +1668,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
span: Span,
opaque_ty_span: Span,
) -> hir::OwnerNode<'hir> {
- let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(opaque_ty_item);
+ let opaque_ty_item_kind = hir::ItemKind::OpaqueTy(self.arena.alloc(opaque_ty_item));
// Generate an `type Foo = impl Trait;` declaration.
trace!("registering opaque type with id {:#?}", opaque_ty_id);
let opaque_ty_item = hir::Item {
@@ -1983,7 +1996,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let lifetime = Lifetime { id: outer_node_id, ident };
collected_lifetimes.push((inner_node_id, lifetime, Some(inner_res)));
}
-
debug!(?collected_lifetimes);
// We only want to capture the lifetimes that appear in the bounds. So visit the bounds to
@@ -1993,22 +2005,36 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let lifetimes_to_remap = lifetime_collector::lifetimes_in_ret_ty(&self.resolver, output);
debug!(?lifetimes_to_remap);
- self.with_hir_id_owner(opaque_ty_node_id, |this| {
- // If this opaque type is only capturing a subset of the lifetimes (those that appear
- // in bounds), then create the new lifetime parameters required and create a mapping
- // from the old `'a` (on the function) to the new `'a` (on the opaque type).
- collected_lifetimes.extend(
- this.create_lifetime_defs(
- opaque_ty_def_id,
- &lifetimes_to_remap,
- &mut new_remapping,
- )
+ // If this opaque type is only capturing a subset of the lifetimes (those that appear in
+ // bounds), then create the new lifetime parameters required and create a mapping from the
+ // old `'a` (on the function) to the new `'a` (on the opaque type).
+ collected_lifetimes.extend(
+ self.create_lifetime_defs(opaque_ty_def_id, &lifetimes_to_remap, &mut new_remapping)
.into_iter()
.map(|(new_node_id, lifetime)| (new_node_id, lifetime, None)),
- );
- debug!(?collected_lifetimes);
- debug!(?new_remapping);
+ );
+ debug!(?collected_lifetimes);
+ debug!(?new_remapping);
+
+ // This creates pairs of HIR lifetimes and def_ids. In the given example `type
+ // TestReturn<'a, T, 'x> = impl Debug + 'x`, it creates a collection containing the
+ // new lifetime of the RPIT 'x and the def_id of the lifetime 'x corresponding to
+ // `TestReturn`.
+ let collected_lifetime_mapping: Vec<_> = collected_lifetimes
+ .iter()
+ .map(|(node_id, lifetime, res)| {
+ let id = self.next_node_id();
+ let res = res.unwrap_or(
+ self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
+ );
+ let lifetime = self.new_named_lifetime_with_res(id, lifetime.ident, res);
+ let def_id = self.local_def_id(*node_id);
+ (lifetime, def_id)
+ })
+ .collect();
+ debug!(?collected_lifetime_mapping);
+ self.with_hir_id_owner(opaque_ty_node_id, |this| {
// Install the remapping from old to new (if any):
this.with_remapping(new_remapping, |this| {
// We have to be careful to get elision right here. The
@@ -2063,6 +2089,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
));
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
+ let lifetime_mapping = if in_trait {
+ self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, def_id)| (**lifetime, *def_id)),
+ )
+ } else {
+ &mut []
+ };
+
let opaque_ty_item = hir::OpaqueTy {
generics: this.arena.alloc(hir::Generics {
params: generic_params,
@@ -2073,6 +2109,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}),
bounds: arena_vec![this; future_bound],
origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
+ lifetime_mapping,
in_trait,
};
@@ -2096,15 +2133,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
//
// For the "output" lifetime parameters, we just want to
// generate `'_`.
- let generic_args = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
- |(_, lifetime, res)| {
- let id = self.next_node_id();
- let res = res.unwrap_or(
- self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error),
- );
- hir::GenericArg::Lifetime(self.new_named_lifetime_with_res(id, lifetime.ident, res))
- },
- ));
+ let generic_args = self.arena.alloc_from_iter(
+ collected_lifetime_mapping
+ .iter()
+ .map(|(lifetime, _)| hir::GenericArg::Lifetime(*lifetime)),
+ );
// Create the `Foo<...>` reference itself. Note that the `type
// Foo = impl Trait` is, internally, created as a child of the
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 04ed27678..096cea945 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -364,7 +364,12 @@ impl<'a> AstValidator<'a> {
self.err_handler().emit_err(errors::BoundInContext { span, ctx });
}
- fn check_foreign_ty_genericless(&self, generics: &Generics, where_span: Span) {
+ fn check_foreign_ty_genericless(
+ &self,
+ generics: &Generics,
+ before_where_clause: &TyAliasWhereClause,
+ after_where_clause: &TyAliasWhereClause,
+ ) {
let cannot_have = |span, descr, remove_descr| {
self.err_handler().emit_err(errors::ExternTypesCannotHave {
span,
@@ -378,9 +383,14 @@ impl<'a> AstValidator<'a> {
cannot_have(generics.span, "generic parameters", "generic parameters");
}
- if !generics.where_clause.predicates.is_empty() {
- cannot_have(where_span, "`where` clauses", "`where` clause");
- }
+ let check_where_clause = |where_clause: &TyAliasWhereClause| {
+ if let TyAliasWhereClause(true, where_clause_span) = where_clause {
+ cannot_have(*where_clause_span, "`where` clauses", "`where` clause");
+ }
+ };
+
+ check_where_clause(before_where_clause);
+ check_where_clause(after_where_clause);
}
fn check_foreign_kind_bodyless(&self, ident: Ident, kind: &str, body: Option<Span>) {
@@ -613,13 +623,12 @@ impl<'a> AstValidator<'a> {
fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) {
// FIXME(davidtwco): This is a hack to detect macros which produce spans of the
// call site which do not have a macro backtrace. See #61963.
- let is_macro_callsite = self
+ if self
.session
.source_map()
.span_to_snippet(span)
- .map(|snippet| snippet.starts_with("#["))
- .unwrap_or(true);
- if !is_macro_callsite {
+ .is_ok_and(|snippet| !snippet.starts_with("#["))
+ {
self.lint_buffer.buffer_lint_with_diagnostic(
MISSING_ABI,
id,
@@ -1039,7 +1048,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(fi.ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
- self.check_foreign_ty_genericless(generics, where_clauses.0.1);
+ self.check_foreign_ty_genericless(generics, &where_clauses.0, &where_clauses.1);
self.check_foreign_item_ascii_only(fi.ident);
}
ForeignItemKind::Static(_, _, body) => {
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 82fe2a21d..ab8015c4a 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -78,13 +78,6 @@ pub struct ForbiddenLifetimeBound {
}
#[derive(Diagnostic)]
-#[diag(ast_passes_forbidden_non_lifetime_param)]
-pub struct ForbiddenNonLifetimeParam {
- #[primary_span]
- pub spans: Vec<Span>,
-}
-
-#[derive(Diagnostic)]
#[diag(ast_passes_fn_param_too_many)]
pub struct FnParamTooMany {
#[primary_span]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 274f931e4..b0dbc2c23 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -2,7 +2,6 @@ use rustc_ast as ast;
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{attr, AssocConstraint, AssocConstraintKind, NodeId};
use rustc_ast::{PatKind, RangeEnd};
-use rustc_errors::{Applicability, StashKey};
use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
use rustc_session::Session;
@@ -374,55 +373,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
}
- fn visit_stmt(&mut self, stmt: &'a ast::Stmt) {
- if let ast::StmtKind::Semi(expr) = &stmt.kind
- && let ast::ExprKind::Assign(lhs, _, _) = &expr.kind
- && let ast::ExprKind::Type(..) = lhs.kind
- && self.sess.parse_sess.span_diagnostic.err_count() == 0
- && !self.features.type_ascription
- && !lhs.span.allows_unstable(sym::type_ascription)
- {
- // When we encounter a statement of the form `foo: Ty = val;`, this will emit a type
- // ascription error, but the likely intention was to write a `let` statement. (#78907).
- feature_err(
- &self.sess.parse_sess,
- sym::type_ascription,
- lhs.span,
- "type ascription is experimental",
- ).span_suggestion_verbose(
- lhs.span.shrink_to_lo(),
- "you might have meant to introduce a new binding",
- "let ",
- Applicability::MachineApplicable,
- ).emit();
- }
- visit::walk_stmt(self, stmt);
- }
-
fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
- ast::ExprKind::Type(..) => {
- if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
- // To avoid noise about type ascription in common syntax errors,
- // only emit if it is the *only* error.
- gate_feature_post!(
- &self,
- type_ascription,
- e.span,
- "type ascription is experimental"
- );
- } else {
- // And if it isn't, cancel the early-pass warning.
- if let Some(err) = self
- .sess
- .parse_sess
- .span_diagnostic
- .steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
- {
- err.cancel()
- }
- }
- }
ast::ExprKind::TryBlock(_) => {
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
}
@@ -603,6 +555,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(dyn_star, "`dyn*` trait objects are experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
+ gate_all!(explicit_tail_calls, "`become` expression is experimental");
if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
@@ -629,7 +582,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(box_patterns, "box pattern syntax is experimental");
gate_all!(exclusive_range_pattern, "exclusive range pattern syntax is experimental");
gate_all!(try_blocks, "`try` blocks are unstable");
- gate_all!(type_ascription, "type ascription is experimental");
visit::walk_crate(&mut visitor, krate);
}
diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs
index ac9e7d06c..83b7e1390 100644
--- a/compiler/rustc_ast_pretty/src/pprust/mod.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs
@@ -32,6 +32,10 @@ pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
State::new().bounds_to_string(bounds)
}
+pub fn where_bound_predicate_to_string(where_bound_predicate: &ast::WhereBoundPredicate) -> String {
+ State::new().where_bound_predicate_to_string(where_bound_predicate)
+}
+
pub fn pat_to_string(pat: &ast::Pat) -> String {
State::new().pat_to_string(pat)
}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 3f80728a2..59239b49e 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -824,6 +824,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
Self::to_string(|s| s.print_type_bounds(bounds))
}
+ fn where_bound_predicate_to_string(
+ &self,
+ where_bound_predicate: &ast::WhereBoundPredicate,
+ ) -> String {
+ Self::to_string(|s| s.print_where_bound_predicate(where_bound_predicate))
+ }
+
fn pat_to_string(&self, pat: &ast::Pat) -> String {
Self::to_string(|s| s.print_pat(pat))
}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index 87c32ffce..609920180 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -537,6 +537,11 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
}
}
+ ast::ExprKind::Become(result) => {
+ self.word("become");
+ self.word(" ");
+ self.print_expr_maybe_paren(result, parser::PREC_JUMP);
+ }
ast::ExprKind::InlineAsm(a) => {
// FIXME: This should have its own syntax, distinct from a macro invocation.
self.word("asm!");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index c465f8c94..5c01b7ea7 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -623,19 +623,8 @@ impl<'a> State<'a> {
pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) {
match predicate {
- ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
- bound_generic_params,
- bounded_ty,
- bounds,
- ..
- }) => {
- self.print_formal_generic_params(bound_generic_params);
- self.print_type(bounded_ty);
- self.word(":");
- if !bounds.is_empty() {
- self.nbsp();
- self.print_type_bounds(bounds);
- }
+ ast::WherePredicate::BoundPredicate(where_bound_predicate) => {
+ self.print_where_bound_predicate(where_bound_predicate);
}
ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
lifetime,
@@ -658,6 +647,19 @@ impl<'a> State<'a> {
}
}
+ pub fn print_where_bound_predicate(
+ &mut self,
+ where_bound_predicate: &ast::WhereBoundPredicate,
+ ) {
+ self.print_formal_generic_params(&where_bound_predicate.bound_generic_params);
+ self.print_type(&where_bound_predicate.bounded_ty);
+ self.word(":");
+ if !where_bound_predicate.bounds.is_empty() {
+ self.nbsp();
+ self.print_type_bounds(&where_bound_predicate.bounds);
+ }
+ }
+
fn print_use_tree(&mut self, tree: &ast::UseTree) {
match &tree.kind {
ast::UseTreeKind::Simple(rename) => {
diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs
index 6be20b097..0b44beeb0 100644
--- a/compiler/rustc_borrowck/src/borrow_set.rs
+++ b/compiler/rustc_borrowck/src/borrow_set.rs
@@ -72,8 +72,11 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
let kind = match self.kind {
mir::BorrowKind::Shared => "",
mir::BorrowKind::Shallow => "shallow ",
- mir::BorrowKind::Unique => "uniq ",
- mir::BorrowKind::Mut { .. } => "mut ",
+ mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
+ // FIXME: differentiate `TwoPhaseBorrow`
+ mir::BorrowKind::Mut {
+ kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
+ } => "mut ",
};
write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
}
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index acca1a147..a4e0e773a 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -278,7 +278,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
move_from_span: Span,
move_from_desc: &str,
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
- struct_span_err!(self, move_from_span, E0507, "cannot move out of {}", move_from_desc,)
+ struct_span_err!(self, move_from_span, E0507, "cannot move out of {}", move_from_desc)
}
/// Signal an error due to an attempt to move out of the interior
diff --git a/compiler/rustc_borrowck/src/constraint_generation.rs b/compiler/rustc_borrowck/src/constraint_generation.rs
index 2aa09a3f2..743d117e0 100644
--- a/compiler/rustc_borrowck/src/constraint_generation.rs
+++ b/compiler/rustc_borrowck/src/constraint_generation.rs
@@ -4,8 +4,8 @@ use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::visit::TyContext;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{
- BasicBlock, BasicBlockData, Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue,
- SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UserTypeProjection,
+ Body, Local, Location, Place, PlaceRef, ProjectionElem, Rvalue, SourceInfo, Statement,
+ StatementKind, Terminator, TerminatorKind, UserTypeProjection,
};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::visit::TypeVisitable;
@@ -49,10 +49,6 @@ struct ConstraintGeneration<'cg, 'tcx> {
}
impl<'cg, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'tcx> {
- fn visit_basic_block_data(&mut self, bb: BasicBlock, data: &BasicBlockData<'tcx>) {
- self.super_basic_block_data(bb, data);
- }
-
/// We sometimes have `substs` within an rvalue, or within a
/// call. Make them live at the location where they appear.
fn visit_substs(&mut self, substs: &SubstsRef<'tcx>, location: Location) {
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index 2daa82aef..1064b44d2 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -59,7 +59,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_before_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -69,7 +69,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -79,7 +79,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_before_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@@ -89,7 +89,7 @@ macro_rules! impl_visitable {
}
fn reconstruct_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@@ -125,15 +125,9 @@ pub struct Borrows<'a, 'tcx> {
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
}
-struct StackEntry {
- bb: mir::BasicBlock,
- lo: usize,
- hi: usize,
-}
-
struct OutOfScopePrecomputer<'a, 'tcx> {
visited: BitSet<mir::BasicBlock>,
- visit_stack: Vec<StackEntry>,
+ visit_stack: Vec<mir::BasicBlock>,
body: &'a Body<'tcx>,
regioncx: &'a RegionInferenceContext<'tcx>,
borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
@@ -158,29 +152,50 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
borrow_region: RegionVid,
first_location: Location,
) {
- // We visit one BB at a time. The complication is that we may start in the
- // middle of the first BB visited (the one containing `first_location`), in which
- // case we may have to later on process the first part of that BB if there
- // is a path back to its start.
-
- // For visited BBs, we record the index of the first statement processed.
- // (In fully processed BBs this index is 0.) Note also that we add BBs to
- // `visited` once they are added to `stack`, before they are actually
- // processed, because this avoids the need to look them up again on
- // completion.
- self.visited.insert(first_location.block);
-
let first_block = first_location.block;
- let mut first_lo = first_location.statement_index;
- let first_hi = self.body[first_block].statements.len();
+ let first_bb_data = &self.body.basic_blocks[first_block];
+
+ // This is the first block, we only want to visit it from the creation of the borrow at
+ // `first_location`.
+ let first_lo = first_location.statement_index;
+ let first_hi = first_bb_data.statements.len();
+
+ if let Some(kill_stmt) = self.regioncx.first_non_contained_inclusive(
+ borrow_region,
+ first_block,
+ first_lo,
+ first_hi,
+ ) {
+ let kill_location = Location { block: first_block, statement_index: kill_stmt };
+ // If region does not contain a point at the location, then add to list and skip
+ // successor locations.
+ debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
+ self.borrows_out_of_scope_at_location
+ .entry(kill_location)
+ .or_default()
+ .push(borrow_index);
+
+ // The borrow is already dead, there is no need to visit other blocks.
+ return;
+ }
- self.visit_stack.push(StackEntry { bb: first_block, lo: first_lo, hi: first_hi });
+ // The borrow is not dead. Add successor BBs to the work list, if necessary.
+ for succ_bb in first_bb_data.terminator().successors() {
+ if self.visited.insert(succ_bb) {
+ self.visit_stack.push(succ_bb);
+ }
+ }
- 'preorder: while let Some(StackEntry { bb, lo, hi }) = self.visit_stack.pop() {
+ // We may end up visiting `first_block` again. This is not an issue: we know at this point
+ // that it does not kill the borrow in the `first_lo..=first_hi` range, so checking the
+ // `0..first_lo` range and the `0..first_hi` range give the same result.
+ while let Some(block) = self.visit_stack.pop() {
+ let bb_data = &self.body[block];
+ let num_stmts = bb_data.statements.len();
if let Some(kill_stmt) =
- self.regioncx.first_non_contained_inclusive(borrow_region, bb, lo, hi)
+ self.regioncx.first_non_contained_inclusive(borrow_region, block, 0, num_stmts)
{
- let kill_location = Location { block: bb, statement_index: kill_stmt };
+ let kill_location = Location { block, statement_index: kill_stmt };
// If region does not contain a point at the location, then add to list and skip
// successor locations.
debug!("borrow {:?} gets killed at {:?}", borrow_index, kill_location);
@@ -188,38 +203,15 @@ impl<'tcx> OutOfScopePrecomputer<'_, 'tcx> {
.entry(kill_location)
.or_default()
.push(borrow_index);
- continue 'preorder;
- }
- // If we process the first part of the first basic block (i.e. we encounter that block
- // for the second time), we no longer have to visit its successors again.
- if bb == first_block && hi != first_hi {
+ // We killed the borrow, so we do not visit this block's successors.
continue;
}
// Add successor BBs to the work list, if necessary.
- let bb_data = &self.body[bb];
- debug_assert!(hi == bb_data.statements.len());
for succ_bb in bb_data.terminator().successors() {
- if !self.visited.insert(succ_bb) {
- if succ_bb == first_block && first_lo > 0 {
- // `succ_bb` has been seen before. If it wasn't
- // fully processed, add its first part to `stack`
- // for processing.
- self.visit_stack.push(StackEntry { bb: succ_bb, lo: 0, hi: first_lo - 1 });
-
- // And update this entry with 0, to represent the
- // whole BB being processed.
- first_lo = 0;
- }
- } else {
- // succ_bb hasn't been seen before. Add it to
- // `stack` for processing.
- self.visit_stack.push(StackEntry {
- bb: succ_bb,
- lo: 0,
- hi: self.body[succ_bb].statements.len(),
- });
+ if self.visited.insert(succ_bb) {
+ self.visit_stack.push(succ_bb);
}
}
}
@@ -343,7 +335,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
type Idx = BorrowIndex;
fn before_statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -352,7 +344,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
location: Location,
@@ -400,7 +392,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn before_terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -409,7 +401,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -426,7 +418,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
}
fn call_return_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index f41795d60..cfcf31fce 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -180,24 +180,25 @@ trait TypeOpInfo<'tcx> {
return;
};
- let placeholder_region = tcx.mk_re_placeholder(ty::Placeholder {
- universe: adjusted_universe.into(),
- bound: placeholder.bound,
- });
-
- let error_region =
- if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
- let adjusted_universe =
- error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
- adjusted_universe.map(|adjusted| {
- tcx.mk_re_placeholder(ty::Placeholder {
- universe: adjusted.into(),
- bound: error_placeholder.bound,
- })
- })
- } else {
- None
- };
+ let placeholder_region = ty::Region::new_placeholder(
+ tcx,
+ ty::Placeholder { universe: adjusted_universe.into(), bound: placeholder.bound },
+ );
+
+ let error_region = if let RegionElement::PlaceholderRegion(error_placeholder) =
+ error_element
+ {
+ let adjusted_universe =
+ error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
+ adjusted_universe.map(|adjusted| {
+ ty::Region::new_placeholder(
+ tcx,
+ ty::Placeholder { universe: adjusted.into(), bound: error_placeholder.bound },
+ )
+ })
+ } else {
+ None
+ };
debug!(?placeholder_region);
@@ -390,7 +391,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
error_region,
&region_constraints,
|vid| ocx.infcx.region_var_origin(vid),
- |vid| ocx.infcx.universe_of_region(ocx.infcx.tcx.mk_re_var(vid)),
+ |vid| ocx.infcx.universe_of_region(ty::Region::new_var(ocx.infcx.tcx, vid)),
)
}
@@ -411,7 +412,7 @@ fn try_extract_error_from_region_constraints<'tcx>(
}
// FIXME: Should this check the universe of the var?
Constraint::VarSubReg(vid, sup) if sup == placeholder_region => {
- Some((infcx.tcx.mk_re_var(vid), cause.clone()))
+ Some((ty::Region::new_var(infcx.tcx, vid), cause.clone()))
}
_ => None,
}
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 15d73ed73..c8c8b72b3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,6 +1,5 @@
-use std::iter;
-
use either::Either;
+use hir::PatField;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{
@@ -14,9 +13,10 @@ use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter::OnlyBodies;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
- self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
- FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
- ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
+ self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
+ FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind, Operand, Place,
+ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
+ VarBindingForm,
};
use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty};
use rustc_middle::util::CallKind;
@@ -27,6 +27,7 @@ use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::ObligationCtxt;
+use std::iter;
use crate::borrow_set::TwoPhaseActivation;
use crate::borrowck_errors;
@@ -677,8 +678,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let tcx = self.infcx.tcx;
// Find out if the predicates show that the type is a Fn or FnMut
- let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder()
+ let find_fn_kind_from_did = |(pred, _): (ty::Clause<'tcx>, _)| {
+ if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder()
&& pred.self_ty() == ty
{
if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
@@ -704,7 +705,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
.explicit_item_bounds(def_id)
.subst_iter_copied(tcx, substs)
- .find_map(find_fn_kind_from_did),
+ .find_map(|(clause, span)| find_fn_kind_from_did((clause, span))),
ty::Closure(_, substs) => match substs.as_closure().kind() {
ty::ClosureKind::Fn => Some(hir::Mutability::Not),
ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
@@ -775,7 +776,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let predicates: Result<Vec<_>, _> = errors
.into_iter()
.map(|err| match err.obligation.predicate.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
+ PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
match predicate.self_ty().kind() {
ty::Param(param_ty) => Ok((
generics.type_param(param_ty, tcx),
@@ -926,7 +927,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// FIXME: supply non-"" `opt_via` when appropriate
let first_borrow_desc;
let mut err = match (gen_borrow_kind, issued_borrow.kind) {
- (BorrowKind::Shared, BorrowKind::Mut { .. }) => {
+ (
+ BorrowKind::Shared,
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
+ ) => {
first_borrow_desc = "mutable ";
self.cannot_reborrow_already_borrowed(
span,
@@ -940,7 +944,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None,
)
}
- (BorrowKind::Mut { .. }, BorrowKind::Shared) => {
+ (
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
+ BorrowKind::Shared,
+ ) => {
first_borrow_desc = "immutable ";
let mut err = self.cannot_reborrow_already_borrowed(
span,
@@ -962,7 +969,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err
}
- (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
+ (
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },
+ ) => {
first_borrow_desc = "first ";
let mut err = self.cannot_mutably_borrow_multiply(
span,
@@ -972,7 +982,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&msg_borrow,
None,
);
- self.suggest_split_at_mut_if_applicable(
+ self.suggest_slice_method_if_applicable(
&mut err,
place,
issued_borrow.borrowed_place,
@@ -982,15 +992,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
issued_borrow.borrowed_place,
&issued_spans,
);
+ self.explain_iterator_advancement_in_for_loop_if_applicable(
+ &mut err,
+ span,
+ &issued_spans,
+ );
err
}
- (BorrowKind::Unique, BorrowKind::Unique) => {
+ (
+ BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
+ BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
+ ) => {
first_borrow_desc = "first ";
self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)
}
- (BorrowKind::Mut { .. } | BorrowKind::Unique, BorrowKind::Shallow) => {
+ (BorrowKind::Mut { .. }, BorrowKind::Shallow) => {
if let Some(immutable_section_description) =
self.classify_immutable_section(issued_borrow.assigned_place)
{
@@ -1004,7 +1022,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
borrow_spans.var_subdiag(
None,
&mut err,
- Some(BorrowKind::Unique),
+ Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }),
|kind, var_span| {
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
@@ -1038,7 +1056,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
- (BorrowKind::Unique, _) => {
+ (BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, _) => {
first_borrow_desc = "first ";
self.cannot_uniquely_borrow_by_one_closure(
span,
@@ -1052,7 +1070,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
- (BorrowKind::Shared, BorrowKind::Unique) => {
+ (BorrowKind::Shared, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => {
first_borrow_desc = "first ";
self.cannot_reborrow_already_uniquely_borrowed(
span,
@@ -1067,7 +1085,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
- (BorrowKind::Mut { .. }, BorrowKind::Unique) => {
+ (BorrowKind::Mut { .. }, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => {
first_borrow_desc = "first ";
self.cannot_reborrow_already_uniquely_borrowed(
span,
@@ -1085,10 +1103,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(BorrowKind::Shared, BorrowKind::Shared | BorrowKind::Shallow)
| (
BorrowKind::Shallow,
- BorrowKind::Mut { .. }
- | BorrowKind::Unique
- | BorrowKind::Shared
- | BorrowKind::Shallow,
+ BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Shallow,
) => unreachable!(),
};
@@ -1252,7 +1267,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
);
}
- fn suggest_split_at_mut_if_applicable(
+ fn suggest_slice_method_if_applicable(
&self,
err: &mut Diagnostic,
place: Place<'tcx>,
@@ -1264,7 +1279,75 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
err.help(
"consider using `.split_at_mut(position)` or similar method to obtain \
two mutable non-overlapping sub-slices",
- );
+ )
+ .help("consider using `.swap(index_1, index_2)` to swap elements at the specified indices");
+ }
+ }
+
+ /// Suggest using `while let` for call `next` on an iterator in a for loop.
+ ///
+ /// For example:
+ /// ```ignore (illustrative)
+ ///
+ /// for x in iter {
+ /// ...
+ /// iter.next()
+ /// }
+ /// ```
+ pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable(
+ &self,
+ err: &mut Diagnostic,
+ span: Span,
+ issued_spans: &UseSpans<'tcx>,
+ ) {
+ let issue_span = issued_spans.args_or_use();
+ let tcx = self.infcx.tcx;
+ let hir = tcx.hir();
+
+ let Some(body_id) = hir.get(self.mir_hir_id()).body_id() else { return };
+ let typeck_results = tcx.typeck(self.mir_def_id());
+
+ struct ExprFinder<'hir> {
+ issue_span: Span,
+ expr_span: Span,
+ body_expr: Option<&'hir hir::Expr<'hir>>,
+ loop_bind: Option<Symbol>,
+ }
+ impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
+ fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+ if let hir::ExprKind::Loop(hir::Block{ stmts: [stmt, ..], ..}, _, hir::LoopSource::ForLoop, _) = ex.kind &&
+ let hir::StmtKind::Expr(hir::Expr{ kind: hir::ExprKind::Match(call, [_, bind, ..], _), ..}) = stmt.kind &&
+ let hir::ExprKind::Call(path, _args) = call.kind &&
+ let hir::ExprKind::Path(hir::QPath::LangItem(LangItem::IteratorNext, _, _, )) = path.kind &&
+ let hir::PatKind::Struct(path, [field, ..], _) = bind.pat.kind &&
+ let hir::QPath::LangItem(LangItem::OptionSome, _, _) = path &&
+ let PatField { pat: hir::Pat{ kind: hir::PatKind::Binding(_, _, ident, ..), .. }, ..} = field &&
+ self.issue_span.source_equal(call.span) {
+ self.loop_bind = Some(ident.name);
+ }
+
+ if let hir::ExprKind::MethodCall(body_call, _recv, ..) = ex.kind &&
+ body_call.ident.name == sym::next && ex.span.source_equal(self.expr_span) {
+ self.body_expr = Some(ex);
+ }
+
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+ let mut finder =
+ ExprFinder { expr_span: span, issue_span, loop_bind: None, body_expr: None };
+ finder.visit_expr(hir.body(body_id).value);
+
+ if let Some(loop_bind) = finder.loop_bind &&
+ let Some(body_expr) = finder.body_expr &&
+ let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) &&
+ let Some(trait_did) = tcx.trait_of_item(def_id) &&
+ tcx.is_diagnostic_item(sym::Iterator, trait_did) {
+ err.note(format!(
+ "a for loop advances the iterator for you, the result is stored in `{}`.",
+ loop_bind
+ ));
+ err.help("if you want to call `next` on a iterator within the loop, consider using `while let`.");
}
}
@@ -1720,18 +1803,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(
Some(name),
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
- ) => self.report_escaping_closure_capture(
- borrow_spans,
- borrow_span,
- &RegionName {
- name: self.synthesize_region_name(),
- source: RegionNameSource::Static,
- },
- ConstraintCategory::CallArgument(None),
- var_or_use_span,
- &format!("`{}`", name),
- "block",
- ),
+ ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
+ .report_escaping_closure_capture(
+ borrow_spans,
+ borrow_span,
+ &RegionName {
+ name: self.synthesize_region_name(),
+ source: RegionNameSource::Static,
+ },
+ ConstraintCategory::CallArgument(None),
+ var_or_use_span,
+ &format!("`{}`", name),
+ "block",
+ ),
(
Some(name),
BorrowExplanation::MustBeValidFor {
@@ -1744,7 +1828,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
span,
..
},
- ) if borrow_spans.for_generator() | borrow_spans.for_closure() => self
+ ) if borrow_spans.for_generator() || borrow_spans.for_closure() => self
.report_escaping_closure_capture(
borrow_spans,
borrow_span,
@@ -2579,7 +2663,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
let tcx = self.infcx.tcx;
if let (
- Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
+ Some(Terminator {
+ kind: TerminatorKind::Call { call_source: CallSource::OverloadedOperator, .. },
+ ..
+ }),
Some((method_did, method_substs)),
) = (
&self.body[loan.reserve_location.block].terminator,
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 1d430a93a..225c38efb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -6,10 +6,10 @@ use rustc_hir::intravisit::Visitor;
use rustc_index::IndexSlice;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::{
- Body, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location, Operand, Place,
- Rvalue, Statement, StatementKind, TerminatorKind,
+ Body, CallSource, CastKind, ConstraintCategory, FakeReadCause, Local, LocalInfo, Location,
+ Operand, Place, Rvalue, Statement, StatementKind, TerminatorKind,
};
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span};
@@ -494,7 +494,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} else if self.was_captured_by_trait_object(borrow) {
LaterUseKind::TraitCapture
} else if location.statement_index == block.statements.len() {
- if let TerminatorKind::Call { func, from_hir_call: true, .. } =
+ if let TerminatorKind::Call { func, call_source: CallSource::Normal, .. } =
&block.terminator().kind
{
// Just point to the function, to reduce the chance of overlapping spans.
@@ -584,7 +584,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
},
// If we see an unsized cast, then if it is our data we should check
// whether it is being cast to a trait object.
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), operand, ty) => {
+ Rvalue::Cast(
+ CastKind::PointerCoercion(PointerCoercion::Unsize),
+ operand,
+ ty,
+ ) => {
match operand {
Operand::Copy(place) | Operand::Move(place) => {
if let Some(from) = place.as_local() {
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 20370e4c6..d292611e6 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -13,8 +13,9 @@ use rustc_index::IndexSlice;
use rustc_infer::infer::LateBoundRegionConversionTime;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
- AggregateKind, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location, Operand, Place,
- PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind,
+ AggregateKind, CallSource, Constant, FakeReadCause, Local, LocalInfo, LocalKind, Location,
+ Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
+ TerminatorKind,
};
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
@@ -414,7 +415,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
if !is_terminator {
continue;
} else if let Some(Terminator {
- kind: TerminatorKind::Call { func, from_hir_call: false, .. },
+ kind:
+ TerminatorKind::Call {
+ func,
+ call_source: CallSource::OverloadedOperator,
+ ..
+ },
..
}) = &bbd.terminator
{
@@ -622,8 +628,7 @@ impl UseSpans<'_> {
err.subdiagnostic(match kind {
Some(kd) => match kd {
rustc_middle::mir::BorrowKind::Shared
- | rustc_middle::mir::BorrowKind::Shallow
- | rustc_middle::mir::BorrowKind::Unique => {
+ | rustc_middle::mir::BorrowKind::Shallow => {
CaptureVarKind::Immut { kind_span: capture_kind_span }
}
@@ -839,7 +844,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
debug!("move_spans: target_temp = {:?}", target_temp);
if let Some(Terminator {
- kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
+ kind: TerminatorKind::Call { fn_span, call_source, .. }, ..
}) = &self.body[location.block].terminator
{
let Some((method_did, method_substs)) =
@@ -859,7 +864,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
method_did,
method_substs,
*fn_span,
- *from_hir_call,
+ call_source.from_hir_call(),
Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
);
@@ -1045,7 +1050,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Some(def_id) => type_known_to_meet_bound_modulo_regions(
&self.infcx,
self.param_env,
- tcx.mk_imm_ref(tcx.lifetimes.re_erased, ty),
+ Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty),
def_id,
),
_ => false,
@@ -1141,6 +1146,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// Avoid pointing to the same function in multiple different
// error messages.
if span != DUMMY_SP && self.fn_self_span_reported.insert(self_arg.span) {
+ self.explain_iterator_advancement_in_for_loop_if_applicable(
+ err,
+ span,
+ &move_spans,
+ );
+
let func = tcx.def_path_str(method_did);
err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
func,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index d0e17bf5a..1f2fefadf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -123,13 +123,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
item_msg = access_place_desc;
debug_assert!(self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_ref());
debug_assert!(is_closure_or_generator(
- Place::ty_from(
- the_place_err.local,
- the_place_err.projection,
- self.body,
- self.infcx.tcx
- )
- .ty
+ the_place_err.ty(self.body, self.infcx.tcx).ty
));
reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
@@ -234,7 +228,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
borrow_spans.var_subdiag(
None,
&mut err,
- Some(mir::BorrowKind::Mut { allow_two_phase_borrow: false }),
+ Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
|_kind, var_span| {
let place = self.describe_any_place(access_place.as_ref());
crate::session_diagnostics::CaptureVarCause::MutableBorrowUsePlaceClosure {
@@ -300,7 +294,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
_,
mir::Rvalue::Ref(
_,
- mir::BorrowKind::Mut { allow_two_phase_borrow: false },
+ mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
_,
),
)),
@@ -416,12 +410,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
_,
) = pat.kind
{
- err.span_suggestion(
- upvar_ident.span,
- "consider changing this to be mutable",
- format!("mut {}", upvar_ident.name),
- Applicability::MachineApplicable,
- );
+ if upvar_ident.name == kw::SelfLower {
+ for (_, node) in self.infcx.tcx.hir().parent_iter(upvar_hir_id) {
+ if let Some(fn_decl) = node.fn_decl() {
+ if !matches!(fn_decl.implicit_self, hir::ImplicitSelfKind::ImmRef | hir::ImplicitSelfKind::MutRef) {
+ err.span_suggestion(
+ upvar_ident.span,
+ "consider changing this to be mutable",
+ format!("mut {}", upvar_ident.name),
+ Applicability::MachineApplicable,
+ );
+ break;
+ }
+ }
+ }
+ } else {
+ err.span_suggestion(
+ upvar_ident.span,
+ "consider changing this to be mutable",
+ format!("mut {}", upvar_ident.name),
+ Applicability::MachineApplicable,
+ );
+ }
}
let tcx = self.infcx.tcx;
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 8ec872e20..617c85174 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -508,7 +508,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
let generic_arg = substs[param_index as usize];
let identity_substs =
InternalSubsts::identity_for_item(self.infcx.tcx, adt.did());
- let base_ty = self.infcx.tcx.mk_adt(*adt, identity_substs);
+ let base_ty = Ty::new_adt(self.infcx.tcx, *adt, identity_substs);
let base_generic_arg = identity_substs[param_index as usize];
let adt_desc = adt.descr();
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index f38e1605f..074f37bed 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -928,7 +928,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
fn any_param_predicate_mentions(
&self,
- predicates: &[ty::Predicate<'tcx>],
+ clauses: &[ty::Clause<'tcx>],
ty: Ty<'tcx>,
region: ty::EarlyBoundRegion,
) -> bool {
@@ -937,10 +937,10 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
&& let ty::Param(_) = ty.kind()
{
- predicates.iter().any(|pred| {
+ clauses.iter().any(|pred| {
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) if data.self_ty() == ty => {}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) if data.projection_ty.self_ty() == ty => {}
+ ty::ClauseKind::Trait(data) if data.self_ty() == ty => {}
+ ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {}
_ => return false,
}
tcx.any_free_region_meets(pred, |r| {
diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs
index b2ff25ecb..0152d89eb 100644
--- a/compiler/rustc_borrowck/src/invalidation.rs
+++ b/compiler/rustc_borrowck/src/invalidation.rs
@@ -128,7 +128,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
destination,
target: _,
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
self.consume_operand(location, func);
@@ -255,7 +255,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
}
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
- BorrowKind::Unique | BorrowKind::Mut { .. } => {
+ BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) {
(Deep, Reservation(wk))
@@ -273,7 +273,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
Mutability::Mut => (
Deep,
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
- allow_two_phase_borrow: false,
+ kind: mir::MutBorrowKind::Default,
})),
),
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
@@ -376,14 +376,11 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
}
(Read(_), BorrowKind::Shallow | BorrowKind::Shared)
- | (
- Read(ReadKind::Borrow(BorrowKind::Shallow)),
- BorrowKind::Unique | BorrowKind::Mut { .. },
- ) => {
+ | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
// Reads don't invalidate shared or shallow borrows
}
- (Read(_), BorrowKind::Unique | BorrowKind::Mut { .. }) => {
+ (Read(_), BorrowKind::Mut { .. }) => {
// Reading from mere reservations of mutable-borrows is OK.
if !is_active(&this.dominators, borrow, location) {
// If the borrow isn't active yet, reads don't invalidate it
@@ -425,7 +422,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
// only mutable borrows should be 2-phase
assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Shallow => false,
- BorrowKind::Unique | BorrowKind::Mut { .. } => true,
+ BorrowKind::Mut { .. } => true,
});
self.access_place(
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index a53ea100c..97d15cb53 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -29,8 +29,8 @@ use rustc_infer::infer::{
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
use rustc_middle::mir::{
- traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
- Place, PlaceElem, PlaceRef, VarDebugInfoContents,
+ traversal, Body, ClearCrossCrate, Local, Location, MutBorrowKind, Mutability,
+ NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, VarDebugInfoContents,
};
use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
@@ -222,7 +222,7 @@ fn do_mir_borrowck<'tcx>(
let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
match MoveData::gather_moves(&body, tcx, param_env) {
- Ok((_, move_data)) => (move_data, Vec::new()),
+ Ok(move_data) => (move_data, Vec::new()),
Err((move_data, move_errors)) => (move_data, move_errors),
};
let promoted_errors = promoted
@@ -368,7 +368,7 @@ fn do_mir_borrowck<'tcx>(
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);
- let results = BorrowckResults {
+ let mut results = BorrowckResults {
ever_inits: flow_ever_inits,
uninits: flow_uninits,
borrows: flow_borrows,
@@ -379,7 +379,7 @@ fn do_mir_borrowck<'tcx>(
rustc_mir_dataflow::visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
- &results,
+ &mut results,
&mut mbcx,
);
@@ -598,11 +598,12 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
// 2. loans made in overlapping scopes do not conflict
// 3. assignments do not affect things loaned out as immutable
// 4. moves do not affect things loaned out in any way
-impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx> {
+impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorrowckCtxt<'cx, 'tcx> {
type FlowState = Flows<'cx, 'tcx>;
fn visit_statement_before_primary_effect(
&mut self,
+ _results: &R,
flow_state: &Flows<'cx, 'tcx>,
stmt: &'cx Statement<'tcx>,
location: Location,
@@ -672,6 +673,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
fn visit_terminator_before_primary_effect(
&mut self,
+ _results: &R,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,
@@ -708,7 +710,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
destination,
target: _,
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
self.consume_operand(loc, (func, span), flow_state);
@@ -782,6 +784,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
fn visit_terminator_after_primary_effect(
&mut self,
+ _results: &R,
flow_state: &Flows<'cx, 'tcx>,
term: &'cx Terminator<'tcx>,
loc: Location,
@@ -1068,10 +1071,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
(Read(_), BorrowKind::Shared | BorrowKind::Shallow)
- | (
- Read(ReadKind::Borrow(BorrowKind::Shallow)),
- BorrowKind::Unique | BorrowKind::Mut { .. },
- ) => Control::Continue,
+ | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
+ Control::Continue
+ }
(Reservation(_), BorrowKind::Shallow | BorrowKind::Shared) => {
// This used to be a future compatibility warning (to be
@@ -1084,7 +1086,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Control::Continue
}
- (Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => {
+ (Read(kind), BorrowKind::Mut { .. }) => {
// Reading from mere reservations of mutable-borrows is OK.
if !is_active(this.dominators(), borrow, location) {
assert!(allow_two_phase_borrow(borrow.kind));
@@ -1191,7 +1193,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
(Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
}
BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
- BorrowKind::Unique | BorrowKind::Mut { .. } => {
+ BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if allow_two_phase_borrow(bk) {
(Deep, Reservation(wk))
@@ -1228,7 +1230,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
Mutability::Mut => (
Deep,
Write(WriteKind::MutableBorrow(BorrowKind::Mut {
- allow_two_phase_borrow: false,
+ kind: MutBorrowKind::Default,
})),
),
Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))),
@@ -1562,7 +1564,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
// only mutable borrows should be 2-phase
assert!(match borrow.kind {
BorrowKind::Shared | BorrowKind::Shallow => false,
- BorrowKind::Unique | BorrowKind::Mut { .. } => true,
+ BorrowKind::Mut { .. } => true,
});
self.access_place(
@@ -1956,16 +1958,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let the_place_err;
match kind {
- Reservation(WriteKind::MutableBorrow(
- borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
- ))
- | Write(WriteKind::MutableBorrow(
- borrow_kind @ (BorrowKind::Unique | BorrowKind::Mut { .. }),
- )) => {
- let is_local_mutation_allowed = match borrow_kind {
- BorrowKind::Unique => LocalMutationIsAllowed::Yes,
- BorrowKind::Mut { .. } => is_local_mutation_allowed,
- BorrowKind::Shared | BorrowKind::Shallow => unreachable!(),
+ Reservation(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind }))
+ | Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: mut_borrow_kind })) => {
+ let is_local_mutation_allowed = match mut_borrow_kind {
+ // `ClosureCapture` is used for mutable variable with a immutable binding.
+ // This is only behaviour difference between `ClosureCapture` and mutable borrows.
+ MutBorrowKind::ClosureCapture => LocalMutationIsAllowed::Yes,
+ MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow => {
+ is_local_mutation_allowed
+ }
};
match self.is_mutable(place.as_ref(), is_local_mutation_allowed) {
Ok(root_place) => {
@@ -2028,12 +2029,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
return false;
}
Read(
- ReadKind::Borrow(
- BorrowKind::Unique
- | BorrowKind::Mut { .. }
- | BorrowKind::Shared
- | BorrowKind::Shallow,
- )
+ ReadKind::Borrow(BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Shallow)
| ReadKind::Copy,
) => {
// Access authorized
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 889acb3ac..b5014a3f4 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -441,7 +441,7 @@ fn for_each_region_constraint<'tcx>(
let subject = match req.subject {
ClosureOutlivesSubject::Region(subject) => format!("{:?}", subject),
ClosureOutlivesSubject::Ty(ty) => {
- format!("{:?}", ty.instantiate(tcx, |vid| tcx.mk_re_var(vid)))
+ format!("{:?}", ty.instantiate(tcx, |vid| ty::Region::new_var(tcx, vid)))
}
};
with_msg(format!("where {}: {:?}", subject, req.outlived_free_region,))?;
diff --git a/compiler/rustc_borrowck/src/place_ext.rs b/compiler/rustc_borrowck/src/place_ext.rs
index d521d0db2..3d7e8c6eb 100644
--- a/compiler/rustc_borrowck/src/place_ext.rs
+++ b/compiler/rustc_borrowck/src/place_ext.rs
@@ -46,11 +46,9 @@ impl<'tcx> PlaceExt<'tcx> for Place<'tcx> {
}
}
- for (i, elem) in self.projection.iter().enumerate() {
- let proj_base = &self.projection[..i];
-
+ for (i, (proj_base, elem)) in self.iter_projections().enumerate() {
if elem == ProjectionElem::Deref {
- let ty = Place::ty_from(self.local, proj_base, body, tcx).ty;
+ let ty = proj_base.ty(body, tcx).ty;
match ty.kind() {
ty::Ref(_, _, hir::Mutability::Not) if i == 0 => {
// For references to thread-local statics, we do need
diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs
index 25c485b81..1217dcb9c 100644
--- a/compiler/rustc_borrowck/src/places_conflict.rs
+++ b/compiler/rustc_borrowck/src/places_conflict.rs
@@ -4,7 +4,9 @@ use crate::ArtificialField;
use crate::Overlap;
use crate::{AccessDepth, Deep, Shallow};
use rustc_hir as hir;
-use rustc_middle::mir::{Body, BorrowKind, Local, Place, PlaceElem, PlaceRef, ProjectionElem};
+use rustc_middle::mir::{
+ Body, BorrowKind, Local, MutBorrowKind, Place, PlaceElem, PlaceRef, ProjectionElem,
+};
use rustc_middle::ty::{self, TyCtxt};
use std::cmp::max;
use std::iter;
@@ -35,7 +37,7 @@ pub fn places_conflict<'tcx>(
tcx,
body,
borrow_place,
- BorrowKind::Mut { allow_two_phase_borrow: true },
+ BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow },
access_place.as_ref(),
AccessDepth::Deep,
bias,
@@ -135,13 +137,11 @@ fn place_components_conflict<'tcx>(
}
// loop invariant: borrow_c is always either equal to access_c or disjoint from it.
- for (i, (borrow_c, &access_c)) in
- iter::zip(borrow_place.projection, access_place.projection).enumerate()
+ for ((borrow_place, borrow_c), &access_c) in
+ iter::zip(borrow_place.iter_projections(), access_place.projection)
{
debug!(?borrow_c, ?access_c);
- let borrow_proj_base = &borrow_place.projection[..i];
-
// Borrow and access path both have more components.
//
// Examples:
@@ -154,15 +154,7 @@ fn place_components_conflict<'tcx>(
// check whether the components being borrowed vs
// accessed are disjoint (as in the second example,
// but not the first).
- match place_projection_conflict(
- tcx,
- body,
- borrow_local,
- borrow_proj_base,
- borrow_c,
- access_c,
- bias,
- ) {
+ match place_projection_conflict(tcx, body, borrow_place, borrow_c, access_c, bias) {
Overlap::Arbitrary => {
// We have encountered different fields of potentially
// the same union - the borrow now partially overlaps.
@@ -193,8 +185,7 @@ fn place_components_conflict<'tcx>(
}
if borrow_place.projection.len() > access_place.projection.len() {
- for (i, elem) in borrow_place.projection[access_place.projection.len()..].iter().enumerate()
- {
+ for (base, elem) in borrow_place.iter_projections().skip(access_place.projection.len()) {
// Borrow path is longer than the access path. Examples:
//
// - borrow of `a.b.c`, access to `a.b`
@@ -203,8 +194,7 @@ fn place_components_conflict<'tcx>(
// our place. This is a conflict if that is a part our
// access cares about.
- let proj_base = &borrow_place.projection[..access_place.projection.len() + i];
- let base_ty = Place::ty_from(borrow_local, proj_base, body, tcx).ty;
+ let base_ty = base.ty(body, tcx).ty;
match (elem, &base_ty.kind(), access) {
(_, _, Shallow(Some(ArtificialField::ArrayLength)))
@@ -308,8 +298,7 @@ fn place_base_conflict(l1: Local, l2: Local) -> Overlap {
fn place_projection_conflict<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
- pi1_local: Local,
- pi1_proj_base: &[PlaceElem<'tcx>],
+ pi1: PlaceRef<'tcx>,
pi1_elem: PlaceElem<'tcx>,
pi2_elem: PlaceElem<'tcx>,
bias: PlaceConflictBias,
@@ -331,7 +320,7 @@ fn place_projection_conflict<'tcx>(
debug!("place_element_conflict: DISJOINT-OR-EQ-FIELD");
Overlap::EqualOrDisjoint
} else {
- let ty = Place::ty_from(pi1_local, pi1_proj_base, body, tcx).ty;
+ let ty = pi1.ty(body, tcx).ty;
if ty.is_union() {
// Different fields of a union, we are basically stuck.
debug!("place_element_conflict: STUCK-UNION");
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 50b246b14..e45d3a2c8 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -1139,7 +1139,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
_ => arg.fold_with(self),
}
});
- tcx.mk_opaque(def_id, tcx.mk_substs_from_iter(substs))
+ Ty::new_opaque(tcx, def_id, tcx.mk_substs_from_iter(substs))
}
}
@@ -1158,7 +1158,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.universal_regions_outlived_by(r_scc)
.filter(|&u_r| !self.universal_regions.is_local_free_region(u_r))
.find(|&u_r| self.eval_equal(u_r, r_vid))
- .map(|u_r| tcx.mk_re_var(u_r))
+ .map(|u_r| ty::Region::new_var(tcx, u_r))
// In the case of a failure, use `ReErased`. We will eventually
// return `None` in this case.
.unwrap_or(tcx.lifetimes.re_erased)
@@ -1355,7 +1355,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let vid = self.to_region_vid(r);
let scc = self.constraint_sccs.scc(vid);
let repr = self.scc_representatives[scc];
- tcx.mk_re_var(repr)
+ ty::Region::new_var(tcx, repr)
})
}
@@ -1779,7 +1779,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
// If not, report an error.
- let member_region = infcx.tcx.mk_re_var(member_region_vid);
+ let member_region = ty::Region::new_var(infcx.tcx, member_region_vid);
errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion {
span: m_c.definition_span,
hidden_ty: m_c.hidden_ty,
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 7fc89e89a..1a227f2d1 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -61,7 +61,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn infer_opaque_types(
&self,
infcx: &InferCtxt<'tcx>,
- opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
+ opaque_ty_decls: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
) -> FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> {
let mut result: FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>> = FxIndexMap::default();
@@ -72,7 +72,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.collect();
debug!(?member_constraints);
- for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls {
+ for (opaque_type_key, concrete_type) in opaque_ty_decls {
let substs = opaque_type_key.substs;
debug!(?concrete_type, ?substs);
@@ -92,7 +92,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
None => {
subst_regions.push(vid);
- infcx.tcx.mk_re_error_with_message(
+ ty::Region::new_error_with_message(
+ infcx.tcx,
concrete_type.span,
"opaque type with non-universal region substs",
)
@@ -142,7 +143,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let ty = infcx.infer_opaque_definition_from_instantiation(
opaque_type_key,
universal_concrete_type,
- origin,
);
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
@@ -158,7 +158,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
)
.emit()
});
- prev.ty = infcx.tcx.ty_error(guar);
+ prev.ty = Ty::new_error(infcx.tcx, guar);
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
@@ -214,7 +214,6 @@ pub trait InferCtxtExt<'tcx> {
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
- origin: OpaqueTyOrigin,
) -> Ty<'tcx>;
}
@@ -247,99 +246,117 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
instantiated_ty: OpaqueHiddenType<'tcx>,
- origin: OpaqueTyOrigin,
) -> Ty<'tcx> {
if let Some(e) = self.tainted_by_errors() {
- return self.tcx.ty_error(e);
+ return Ty::new_error(self.tcx, e);
+ }
+
+ if let Err(guar) =
+ check_opaque_type_parameter_valid(self.tcx, opaque_type_key, instantiated_ty.span)
+ {
+ return Ty::new_error(self.tcx, guar);
}
let definition_ty = instantiated_ty
.remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
.ty;
- if let Err(guar) = check_opaque_type_parameter_valid(
+ // `definition_ty` does not live in of the current inference context,
+ // so lets make sure that we don't accidentally misuse our current `infcx`.
+ match check_opaque_type_well_formed(
self.tcx,
- opaque_type_key,
- origin,
+ self.next_trait_solver(),
+ opaque_type_key.def_id,
instantiated_ty.span,
+ definition_ty,
) {
- return self.tcx.ty_error(guar);
+ Ok(hidden_ty) => hidden_ty,
+ Err(guar) => Ty::new_error(self.tcx, guar),
}
+ }
+}
- // Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs`
- // on stable and we'd break that.
- let OpaqueTyOrigin::TyAlias { .. } = origin else {
- return definition_ty;
- };
- let def_id = opaque_type_key.def_id;
- // This logic duplicates most of `check_opaque_meets_bounds`.
- // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
- let param_env = self.tcx.param_env(def_id);
- // HACK This bubble is required for this tests to pass:
- // nested-return-type2-tait2.rs
- // nested-return-type2-tait3.rs
- let infcx =
- self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).build();
- let ocx = ObligationCtxt::new(&infcx);
- // Require the hidden type to be well-formed with only the generics of the opaque type.
- // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
- // hidden type is well formed even without those bounds.
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into()));
-
- let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id);
+/// This logic duplicates most of `check_opaque_meets_bounds`.
+/// FIXME(oli-obk): Also do region checks here and then consider removing
+/// `check_opaque_meets_bounds` entirely.
+fn check_opaque_type_well_formed<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ next_trait_solver: bool,
+ def_id: LocalDefId,
+ definition_span: Span,
+ definition_ty: Ty<'tcx>,
+) -> Result<Ty<'tcx>, ErrorGuaranteed> {
+ // Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs`
+ // on stable and we'd break that.
+ let opaque_ty_hir = tcx.hir().expect_item(def_id);
+ let OpaqueTyOrigin::TyAlias { .. } = opaque_ty_hir.expect_opaque_ty().origin else {
+ return Ok(definition_ty);
+ };
+ let param_env = tcx.param_env(def_id);
+ // HACK This bubble is required for this tests to pass:
+ // nested-return-type2-tait2.rs
+ // nested-return-type2-tait3.rs
+ // FIXME(-Ztrait-solver=next): We probably should use `DefiningAnchor::Error`
+ // and prepopulate this `InferCtxt` with known opaque values, rather than
+ // using the `Bind` anchor here. For now it's fine.
+ let infcx = tcx
+ .infer_ctxt()
+ .with_next_trait_solver(next_trait_solver)
+ .with_opaque_type_inference(if next_trait_solver {
+ DefiningAnchor::Bind(def_id)
+ } else {
+ DefiningAnchor::Bubble
+ })
+ .build();
+ let ocx = ObligationCtxt::new(&infcx);
+ let identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
- // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
- // the bounds that the function supplies.
- let opaque_ty = self.tcx.mk_opaque(def_id.to_def_id(), id_substs);
- if let Err(err) = ocx.eq(
- &ObligationCause::misc(instantiated_ty.span, def_id),
- param_env,
- opaque_ty,
- definition_ty,
- ) {
+ // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
+ // the bounds that the function supplies.
+ let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_substs);
+ ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
+ .map_err(|err| {
infcx
.err_ctxt()
.report_mismatched_types(
- &ObligationCause::misc(instantiated_ty.span, def_id),
+ &ObligationCause::misc(definition_span, def_id),
opaque_ty,
definition_ty,
err,
)
- .emit();
- }
+ .emit()
+ })?;
- ocx.register_obligation(Obligation::misc(
- infcx.tcx,
- instantiated_ty.span,
- def_id,
- param_env,
- predicate,
- ));
+ // Require the hidden type to be well-formed with only the generics of the opaque type.
+ // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
+ // hidden type is well formed even without those bounds.
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ definition_ty.into(),
+ )));
+ ocx.register_obligation(Obligation::misc(tcx, definition_span, def_id, param_env, predicate));
- // Check that all obligations are satisfied by the implementation's
- // version.
- let errors = ocx.select_all_or_error();
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
- // This is still required for many(half of the tests in ui/type-alias-impl-trait)
- // tests to pass
- let _ = infcx.take_opaque_types();
+ // This is still required for many(half of the tests in ui/type-alias-impl-trait)
+ // tests to pass
+ let _ = infcx.take_opaque_types();
- if errors.is_empty() {
- definition_ty
- } else {
- let reported = infcx.err_ctxt().report_fulfillment_errors(&errors);
- self.tcx.ty_error(reported)
- }
+ if errors.is_empty() {
+ Ok(definition_ty)
+ } else {
+ Err(infcx.err_ctxt().report_fulfillment_errors(&errors))
}
}
fn check_opaque_type_parameter_valid(
tcx: TyCtxt<'_>,
opaque_type_key: OpaqueTypeKey<'_>,
- origin: OpaqueTyOrigin,
span: Span,
) -> Result<(), ErrorGuaranteed> {
- match origin {
+ let opaque_ty_hir = tcx.hir().expect_item(opaque_type_key.def_id);
+ match opaque_ty_hir.expect_opaque_ty().origin {
// No need to check return position impl trait (RPIT)
// because for type and const parameters they are correct
// by construction: we convert
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index f527eee7b..c19fbf20c 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -89,11 +89,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
category: ConstraintCategory<'tcx>,
) {
self.prove_predicate(
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: ty::ImplPolarity::Positive,
- }))),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Trait(
+ ty::TraitPredicate {
+ trait_ref,
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ },
+ ))),
locations,
category,
);
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index c8ec1257d..f22851d76 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -245,7 +245,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
.and(type_op::normalize::Normalize::new(ty))
.fully_perform(self.infcx, span)
.unwrap_or_else(|guar| TypeOpOutput {
- output: self.infcx.tcx.ty_error(guar),
+ output: Ty::new_error(self.infcx.tcx, guar),
constraints: None,
error_info: None,
});
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index a06d4bcc6..eec886b7b 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -124,21 +124,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// Return types are a bit more complex. They may contain opaque `impl Trait` types.
let mir_output_ty = body.local_decls[RETURN_PLACE].ty;
let output_span = body.local_decls[RETURN_PLACE].source_info.span;
- if let Err(terr) = self.eq_types(
- normalized_output_ty,
- mir_output_ty,
- Locations::All(output_span),
- ConstraintCategory::BoringNoLocation,
- ) {
- span_mirbug!(
- self,
- Location::START,
- "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`",
- normalized_output_ty,
- mir_output_ty,
- terr
- );
- };
+ self.equate_normalized_input_or_output(normalized_output_ty, mir_output_ty, output_span);
}
#[instrument(skip(self), level = "debug")]
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index dc5121e1a..a15e1065c 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -7,7 +7,6 @@ use std::{fmt, iter, mem};
use either::Either;
-use hir::OpaqueTyOrigin;
use rustc_data_structures::frozen::Frozen;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::ErrorGuaranteed;
@@ -28,7 +27,8 @@ use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::AssertKind;
use rustc_middle::mir::*;
use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::CastTy;
use rustc_middle::ty::subst::{SubstsRef, UserSubsts};
use rustc_middle::ty::visit::TypeVisitableExt;
@@ -50,7 +50,6 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
-use crate::renumber::RegionCtxt;
use crate::session_diagnostics::MoveUnsized;
use crate::{
borrow_set::BorrowSet,
@@ -139,7 +138,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
upvars: &[Upvar<'tcx>],
use_polonius: bool,
) -> MirTypeckResults<'tcx> {
- let implicit_region_bound = infcx.tcx.mk_re_var(universal_regions.fr_fn_body);
+ let implicit_region_bound = ty::Region::new_var(infcx.tcx, universal_regions.fr_fn_body);
let mut constraints = MirTypeckRegionConstraints {
placeholder_indices: PlaceholderIndices::default(),
placeholder_index_to_region: IndexVec::default(),
@@ -188,10 +187,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
// FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
// predefined opaques in the typeck root.
- // FIXME(-Ztrait-solver=next): This is also totally wrong for TAITs, since
- // the HIR typeck map defining usages back to their definition params,
- // they won't actually match up with the usages in this body...
- if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
+ if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
checker.register_predefined_opaques_in_new_solver();
}
@@ -241,10 +237,10 @@ pub(crate) fn type_check<'mir, 'tcx>(
decl.hidden_type.span,
format!("could not resolve {:#?}", hidden_type.ty.kind()),
);
- hidden_type.ty = infcx.tcx.ty_error(reported);
+ hidden_type.ty = Ty::new_error(infcx.tcx, reported);
}
- (opaque_type_key, (hidden_type, decl.origin))
+ (opaque_type_key, hidden_type)
})
.collect();
@@ -480,9 +476,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
fn visit_body(&mut self, body: &Body<'tcx>) {
self.sanitize_type(&"return type", body.return_ty());
- for local_decl in &body.local_decls {
- self.sanitize_type(local_decl, local_decl.ty);
- }
+ // The types of local_decls are checked above which is called in super_body.
self.super_body(body);
}
}
@@ -526,7 +520,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
for elem in place.projection.iter() {
if place_ty.variant_index.is_none() {
if let Err(guar) = place_ty.ty.error_reported() {
- return PlaceTy::from_ty(self.tcx().ty_error(guar));
+ return PlaceTy::from_ty(Ty::new_error(self.tcx(), guar));
}
}
place_ty = self.sanitize_projection(place_ty, elem, place, location, context);
@@ -662,7 +656,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
PlaceTy::from_ty(match base_ty.kind() {
ty::Array(inner, _) => {
assert!(!from_end, "array subslices should not use from_end");
- tcx.mk_array(*inner, to - from)
+ Ty::new_array(tcx, *inner, to - from)
}
ty::Slice(..) => {
assert!(from_end, "slice subslices should use from_end");
@@ -755,7 +749,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
}
fn error(&mut self) -> Ty<'tcx> {
- self.tcx().ty_error_misc()
+ Ty::new_misc_error(self.tcx())
}
fn get_ambient_variance(&self, context: PlaceContext) -> ty::Variance {
@@ -883,8 +877,7 @@ struct BorrowCheckContext<'a, 'tcx> {
pub(crate) struct MirTypeckResults<'tcx> {
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
- pub(crate) opaque_type_values:
- FxIndexMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
+ pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
}
/// A collection of region constraints that must be satisfied for the
@@ -1042,16 +1035,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.typeck(self.body.source.def_id().expect_local())
.concrete_opaque_types
.iter()
- .map(|(&def_id, &hidden_ty)| {
- let substs = ty::InternalSubsts::identity_for_item(self.infcx.tcx, def_id);
- (ty::OpaqueTypeKey { def_id, substs }, hidden_ty)
- })
+ .map(|(k, v)| (*k, *v))
.collect();
let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| {
- self.infcx.next_nll_region_var(
+ self.infcx.next_nll_region_var_in_universe(
NllRegionVariableOrigin::Existential { from_forall: false },
- || RegionCtxt::Unknown,
+ ty::UniverseIndex::ROOT,
)
});
@@ -1061,15 +1051,29 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|ocx| {
- for (key, hidden_ty) in renumbered_opaques {
- ocx.register_infer_ok_obligations(
- ocx.infcx.register_hidden_type_in_new_solver(
- key,
- param_env,
- hidden_ty.ty,
- )?,
+ let mut obligations = Vec::new();
+ for (opaque_type_key, hidden_ty) in renumbered_opaques {
+ let cause = ObligationCause::dummy();
+ ocx.infcx.insert_hidden_type(
+ opaque_type_key,
+ &cause,
+ param_env,
+ hidden_ty.ty,
+ true,
+ &mut obligations,
+ )?;
+
+ ocx.infcx.add_item_bounds_for_hidden_type(
+ opaque_type_key.def_id.to_def_id(),
+ opaque_type_key.substs,
+ cause,
+ param_env,
+ hidden_ty.ty,
+ &mut obligations,
);
}
+
+ ocx.register_obligations(obligations);
Ok(())
},
"register pre-defined opaques",
@@ -1366,7 +1370,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
// FIXME: check the values
}
- TerminatorKind::Call { func, args, destination, from_hir_call, target, .. } => {
+ TerminatorKind::Call { func, args, destination, call_source, target, .. } => {
self.check_operand(func, term_location);
for arg in args {
self.check_operand(arg, term_location);
@@ -1415,9 +1419,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
//
// See #91068 for an example.
self.prove_predicates(
- sig.inputs_and_output
- .iter()
- .map(|ty| ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()))),
+ sig.inputs_and_output.iter().map(|ty| {
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ ty.into(),
+ )))
+ }),
term_location.to_locations(),
ConstraintCategory::Boring,
);
@@ -1440,7 +1446,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.add_element(region_vid, term_location);
}
- self.check_call_inputs(body, term, &sig, args, term_location, *from_hir_call);
+ self.check_call_inputs(body, term, &sig, args, term_location, *call_source);
}
TerminatorKind::Assert { cond, msg, .. } => {
self.check_operand(cond, term_location);
@@ -1567,7 +1573,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
sig: &ty::FnSig<'tcx>,
args: &[Operand<'tcx>],
term_location: Location,
- from_hir_call: bool,
+ call_source: CallSource,
) {
debug!("check_call_inputs({:?}, {:?})", sig, args);
if args.len() < sig.inputs().len() || (args.len() > sig.inputs().len() && !sig.c_variadic) {
@@ -1585,7 +1591,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let op_arg_ty = op_arg.ty(body, self.tcx());
let op_arg_ty = self.normalize(op_arg_ty, term_location);
- let category = if from_hir_call {
+ let category = if call_source.from_hir_call() {
ConstraintCategory::CallArgument(self.infcx.tcx.erase_regions(func_ty))
} else {
ConstraintCategory::Boring
@@ -1846,7 +1852,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let array_ty = rvalue.ty(body.local_decls(), tcx);
self.prove_predicate(
- ty::PredicateKind::WellFormed(array_ty.into()),
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(array_ty.into())),
Locations::Single(location),
ConstraintCategory::Boring,
);
@@ -1902,7 +1908,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.check_operand(op, location);
match cast_kind {
- CastKind::Pointer(PointerCast::ReifyFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
// The type that we see in the fcx is like
@@ -1912,7 +1918,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// and hence may contain unnormalized results.
let fn_sig = self.normalize(fn_sig, location);
- let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
+ let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
if let Err(terr) = self.eq_types(
*ty,
@@ -1931,12 +1937,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
- CastKind::Pointer(PointerCast::ClosureFnPointer(unsafety)) => {
+ CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(unsafety)) => {
let sig = match op.ty(body, tcx).kind() {
ty::Closure(_, substs) => substs.as_closure().sig(),
_ => bug!(),
};
- let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
+ let ty_fn_ptr_from =
+ Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, *unsafety));
if let Err(terr) = self.eq_types(
*ty,
@@ -1955,7 +1962,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
- CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => {
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
// The type that we see in the fcx is like
@@ -1984,7 +1991,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
- CastKind::Pointer(PointerCast::Unsize) => {
+ CastKind::PointerCoercion(PointerCoercion::Unsize) => {
let &ty = ty;
let trait_ref = ty::TraitRef::from_lang_item(
tcx,
@@ -2019,10 +2026,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::Cast,
);
- let outlives_predicate =
- tcx.mk_predicate(Binder::dummy(ty::PredicateKind::Clause(
- ty::Clause::TypeOutlives(ty::OutlivesPredicate(self_ty, *region)),
- )));
+ let outlives_predicate = tcx.mk_predicate(Binder::dummy(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
+ ty::OutlivesPredicate(self_ty, *region),
+ )),
+ ));
self.prove_predicate(
outlives_predicate,
location.to_locations(),
@@ -2030,7 +2038,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
);
}
- CastKind::Pointer(PointerCast::MutToConstPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::MutToConstPointer) => {
let ty::RawPtr(ty::TypeAndMut {
ty: ty_from,
mutbl: hir::Mutability::Mut,
@@ -2072,7 +2080,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
- CastKind::Pointer(PointerCast::ArrayToPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::ArrayToPointer) => {
let ty_from = op.ty(body, tcx);
let opt_ty_elem_mut = match ty_from.kind() {
@@ -2495,7 +2503,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
location, borrow_region, borrowed_place
);
- let mut cursor = borrowed_place.projection.as_ref();
let tcx = self.infcx.tcx;
let field = path_utils::is_upvar_field_projection(
tcx,
@@ -2509,14 +2516,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::Boring
};
- while let [proj_base @ .., elem] = cursor {
- cursor = proj_base;
-
+ for (base, elem) in borrowed_place.as_ref().iter_projections().rev() {
debug!("add_reborrow_constraint - iteration {:?}", elem);
match elem {
ProjectionElem::Deref => {
- let base_ty = Place::ty_from(borrowed_place.local, proj_base, body, tcx).ty;
+ let base_ty = base.ty(body, tcx).ty;
debug!("add_reborrow_constraint - base_ty = {:?}", base_ty);
match base_ty.kind() {
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 56f078f2d..7821b82bf 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -500,7 +500,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
.next_nll_region_var(FR, || RegionCtxt::Free(Symbol::intern("c-variadic")))
.as_var();
- let region = self.infcx.tcx.mk_re_var(reg_vid);
+ let region = ty::Region::new_var(self.infcx.tcx, reg_vid);
let va_list_ty =
self.infcx.tcx.type_of(va_list_did).subst(self.infcx.tcx, &[region.into()]);
@@ -660,7 +660,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BrEnv,
};
- let env_region = tcx.mk_re_late_bound(ty::INNERMOST, br);
+ let env_region = ty::Region::new_late_bound(tcx, ty::INNERMOST, br);
let closure_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
// The "inputs" of the closure in the
@@ -685,7 +685,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
assert_eq!(self.mir_def.to_def_id(), def_id);
let resume_ty = substs.as_generator().resume_ty();
let output = substs.as_generator().return_ty();
- let generator_ty = tcx.mk_generator(def_id, substs, movability);
+ let generator_ty = Ty::new_generator(tcx, def_id, substs, movability);
let inputs_and_output =
self.infcx.tcx.mk_type_list(&[generator_ty, resume_ty, output]);
ty::Binder::dummy(inputs_and_output)
@@ -778,7 +778,8 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for BorrowckInferCtxt<'cx, 'tcx> {
{
let (value, _map) = self.tcx.replace_late_bound_regions(value, |br| {
debug!(?br);
- let liberated_region = self.tcx.mk_re_free(all_outlive_scope.to_def_id(), br.kind);
+ let liberated_region =
+ ty::Region::new_free(self.tcx, all_outlive_scope.to_def_id(), br.kind);
let region_vid = {
let name = match br.kind.get_name() {
Some(name) => name,
@@ -889,7 +890,7 @@ impl<'tcx> UniversalRegionIndices<'tcx> {
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
- tcx.fold_regions(value, |region, _| tcx.mk_re_var(self.to_region_vid(region)))
+ tcx.fold_regions(value, |region, _| ty::Region::new_var(tcx, self.to_region_vid(region)))
}
}
@@ -929,7 +930,7 @@ fn for_each_late_bound_region_in_item<'tcx>(
for bound_var in tcx.late_bound_vars(tcx.hir().local_def_id_to_hir_id(mir_def_id)) {
let ty::BoundVariableKind::Region(bound_region) = bound_var else { continue; };
- let liberated_region = tcx.mk_re_free(mir_def_id.to_def_id(), bound_region);
+ let liberated_region = ty::Region::new_free(tcx, mir_def_id.to_def_id(), bound_region);
f(liberated_region);
}
}
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index f00cd39cb..322222ae3 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -1,4 +1,5 @@
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
+builtin_macros_alloc_must_statics = allocators must be statics
builtin_macros_asm_clobber_abi = clobber_abi
builtin_macros_asm_clobber_no_reg = asm with `clobber_abi` must specify explicit registers for outputs
@@ -56,6 +57,9 @@ builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `e
.label = not applicable here
.label2 = not a `struct`, `enum` or `union`
+builtin_macros_bench_sig = functions used as benches must have signature `fn(&mut Bencher) -> impl Termination`
+
+
builtin_macros_cannot_derive_union = this trait cannot be derived for unions
builtin_macros_cfg_accessible_has_args = `cfg_accessible` path cannot accept arguments
@@ -84,6 +88,7 @@ builtin_macros_concat_bytes_non_u8 = numeric literal is not a `u8`
builtin_macros_concat_bytes_oob = numeric literal is out of bounds
builtin_macros_concat_bytestr = cannot concatenate a byte string literal
+builtin_macros_concat_c_str_lit = cannot concatenate a C string literal
builtin_macros_concat_idents_ident_args = `concat_idents!()` requires ident args
@@ -111,6 +116,10 @@ builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
+builtin_macros_expected_register_class_or_explicit_register = expected register class or explicit register
+
+builtin_macros_export_macro_rules = cannot export macro_rules! macros from a `proc-macro` crate type currently
+
builtin_macros_format_duplicate_arg = duplicate argument named `{$ident}`
.label1 = previously here
.label2 = duplicate argument
@@ -158,6 +167,8 @@ builtin_macros_format_unused_args = multiple unused formatting arguments
builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
+builtin_macros_invalid_crate_attribute = invalid crate attribute
+
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
.note = only one `#[default]` attribute is needed
.label = `#[default]` used here
@@ -177,6 +188,8 @@ builtin_macros_no_default_variant = no default declared
.help = make a unit variant default by placing `#[default]` above it
.suggestion = make `{$ident}` default
+builtin_macros_non_abi = at least one abi must be provided as an argument to `clobber_abi`
+
builtin_macros_non_exhaustive_default = default variant must be exhaustive
.label = declared `#[non_exhaustive]` here
.help = consider a manual implementation of `Default`
@@ -184,12 +197,20 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
.help = consider a manual implementation of `Default`
+builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
+
builtin_macros_requires_cfg_pattern =
macro requires a cfg-pattern as an argument
.label = cfg-pattern required
+builtin_macros_should_panic = functions using `#[should_panic]` must return `()`
+
builtin_macros_sugg = consider using a positional formatting argument instead
+builtin_macros_test_arg_non_lifetime = functions used as tests can not have any non-lifetime generic parameters
+
+builtin_macros_test_args = functions used as tests can not have any arguments
+
builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
.label = `{$kind}` because of this
@@ -198,6 +219,10 @@ builtin_macros_test_case_non_item = `#[test_case]` attribute is only allowed on
builtin_macros_test_runner_invalid = `test_runner` argument must be a path
builtin_macros_test_runner_nargs = `#![test_runner(..)]` accepts exactly 1 argument
+builtin_macros_tests_not_support = building tests with panic=abort is not supported without `-Zpanic_abort_tests`
+
+builtin_macros_trace_macros = trace_macros! accepts only `true` or `false`
+
builtin_macros_unexpected_lit = expected path to a trait, found literal
.label = not a trait
.str_lit = try using `#[derive({$sym})]`
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 5217e317a..6187e4f51 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -371,24 +371,16 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
- let err = p.sess.span_diagnostic.struct_span_err(
- p.token.span,
- "at least one abi must be provided as an argument to `clobber_abi`",
- );
- return Err(err);
+ return Err(p.sess.span_diagnostic.create_err(errors::NonABI { span: p.token.span }));
}
let mut new_abis = Vec::new();
- loop {
+ while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
match p.parse_str_lit() {
Ok(str_lit) => {
new_abis.push((str_lit.symbol_unescaped, str_lit.span));
}
Err(opt_lit) => {
- // If the non-string literal is a closing paren then it's the end of the list and is fine
- if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
- break;
- }
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
let mut err =
p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
@@ -432,9 +424,9 @@ fn parse_reg<'a>(
ast::InlineAsmRegOrRegClass::Reg(symbol)
}
_ => {
- return Err(
- p.struct_span_err(p.token.span, "expected register class or explicit register")
- );
+ return Err(p.sess.create_err(errors::ExpectedRegisterClassOrExplicitRegister {
+ span: p.token.span,
+ }));
}
};
p.bump();
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index b619e80e1..902c1e40c 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -320,6 +320,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::Underscore
| ExprKind::While(_, _, _)
| ExprKind::Yeet(_)
+ | ExprKind::Become(_)
| ExprKind::Yield(_) => {}
}
}
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 2b6fcc169..7b75d7d84 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -1,5 +1,6 @@
//! Attributes injected into the crate root from command line using `-Z crate-attr`.
+use crate::errors;
use rustc_ast::attr::mk_attr;
use rustc_ast::token;
use rustc_ast::{self as ast, AttrItem, AttrStyle};
@@ -24,7 +25,9 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
};
let end_span = parser.token.span;
if parser.token != token::Eof {
- parse_sess.span_diagnostic.span_err(start_span.to(end_span), "invalid crate attribute");
+ parse_sess
+ .span_diagnostic
+ .emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
continue;
}
diff --git a/compiler/rustc_builtin_macros/src/compile_error.rs b/compiler/rustc_builtin_macros/src/compile_error.rs
index aeb3bb800..5efc5a4e3 100644
--- a/compiler/rustc_builtin_macros/src/compile_error.rs
+++ b/compiler/rustc_builtin_macros/src/compile_error.rs
@@ -18,7 +18,7 @@ pub fn expand_compile_error<'cx>(
reason = "diagnostic message is specified by user"
)]
#[expect(rustc::untranslatable_diagnostic, reason = "diagnostic message is specified by user")]
- cx.span_err(sp, var.as_str());
+ cx.span_err(sp, var.to_string());
DummyResult::any(sp)
}
diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs
index 50e88ae2e..9695fb4fe 100644
--- a/compiler/rustc_builtin_macros/src/concat.rs
+++ b/compiler/rustc_builtin_macros/src/concat.rs
@@ -33,7 +33,7 @@ pub fn expand_concat(
accumulator.push_str(&b.to_string());
}
Ok(ast::LitKind::CStr(..)) => {
- cx.span_err(e.span, "cannot concatenate a C string literal");
+ cx.emit_err(errors::ConcatCStrLit{ span: e.span});
has_errors = true;
}
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 5ef35af0a..6a1586f07 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -21,7 +21,7 @@ fn invalid_type_err(
Ok(ast::LitKind::CStr(_, _)) => {
// FIXME(c_str_literals): should concatenation of C string literals
// include the null bytes in the end?
- cx.span_err(span, "cannot concatenate C string literals");
+ cx.emit_err(errors::ConcatCStrLit { span: span });
}
Ok(ast::LitKind::Char(_)) => {
let sugg =
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index fe4483104..140853db6 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -8,7 +8,7 @@ use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident};
-use rustc_span::Span;
+use rustc_span::{ErrorGuaranteed, Span};
pub(crate) struct Expander(pub bool);
@@ -22,7 +22,7 @@ impl MultiItemModifier for Expander {
_: bool,
) -> ExpandResult<Vec<Annotatable>, Annotatable> {
let sess = ecx.sess;
- if report_bad_target(sess, &item, span) {
+ if report_bad_target(sess, &item, span).is_err() {
// We don't want to pass inappropriate targets to derive macros to avoid
// follow up errors, all other errors below are recoverable.
return ExpandResult::Ready(vec![item]);
@@ -103,7 +103,11 @@ fn dummy_annotatable() -> Annotatable {
})
}
-fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
+fn report_bad_target(
+ sess: &Session,
+ item: &Annotatable,
+ span: Span,
+) -> Result<(), ErrorGuaranteed> {
let item_kind = match item {
Annotatable::Item(item) => Some(&item.kind),
Annotatable::Stmt(stmt) => match &stmt.kind {
@@ -116,9 +120,9 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
let bad_target =
!matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
if bad_target {
- sess.emit_err(errors::BadDeriveTarget { span, item: item.span() });
+ return Err(sess.emit_err(errors::BadDeriveTarget { span, item: item.span() }));
}
- bad_target
+ Ok(())
}
fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 988356374..9ba98d0a5 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -68,7 +68,6 @@ pub fn expand_deriving_clone(
_ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
}
- let attrs = thin_vec![cx.attr_word(sym::inline, span)];
let trait_def = TraitDef {
span,
path: path_std!(clone::Clone),
@@ -82,7 +81,7 @@ pub fn expand_deriving_clone(
explicit_self: true,
nonself_args: Vec::new(),
ret_ty: Self_,
- attributes: attrs,
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: substructure,
}],
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index af9719586..c78a0eb04 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -18,11 +18,6 @@ pub fn expand_deriving_eq(
is_const: bool,
) {
let span = cx.with_def_site_ctxt(span);
- let attrs = thin_vec![
- cx.attr_word(sym::inline, span),
- cx.attr_nested_word(sym::doc, sym::hidden, span),
- cx.attr_word(sym::no_coverage, span)
- ];
let trait_def = TraitDef {
span,
path: path_std!(cmp::Eq),
@@ -36,7 +31,11 @@ pub fn expand_deriving_eq(
explicit_self: true,
nonself_args: vec![],
ret_ty: Unit,
- attributes: attrs,
+ attributes: thin_vec![
+ cx.attr_word(sym::inline, span),
+ cx.attr_nested_word(sym::doc, sym::hidden, span),
+ cx.attr_word(sym::no_coverage, span)
+ ],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
cs_total_eq_assert(a, b, c)
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
index cfd36f030..4401cf8a9 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs
@@ -15,7 +15,6 @@ pub fn expand_deriving_ord(
push: &mut dyn FnMut(Annotatable),
is_const: bool,
) {
- let attrs = thin_vec![cx.attr_word(sym::inline, span)];
let trait_def = TraitDef {
span,
path: path_std!(cmp::Ord),
@@ -29,7 +28,7 @@ pub fn expand_deriving_ord(
explicit_self: true,
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_std!(cmp::Ordering)),
- attributes: attrs,
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
}],
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
index bad47db0d..a71ecc5db 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
@@ -82,14 +82,13 @@ pub fn expand_deriving_partial_eq(
// No need to generate `ne`, the default suffices, and not generating it is
// faster.
- let attrs = thin_vec![cx.attr_word(sym::inline, span)];
let methods = vec![MethodDef {
name: sym::eq,
generics: Bounds::empty(),
explicit_self: true,
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_local!(bool)),
- attributes: attrs,
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
}];
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
index 9f4624790..54b6cb7d7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs
@@ -19,8 +19,6 @@ pub fn expand_deriving_partial_ord(
let ret_ty =
Path(Path::new_(pathvec_std!(option::Option), vec![Box::new(ordering_ty)], PathKind::Std));
- let attrs = thin_vec![cx.attr_word(sym::inline, span)];
-
// Order in which to perform matching
let tag_then_data = if let Annotatable::Item(item) = item
&& let ItemKind::Enum(def, _) = &item.kind {
@@ -48,7 +46,7 @@ pub fn expand_deriving_partial_ord(
explicit_self: true,
nonself_args: vec![(self_ref(), sym::other)],
ret_ty,
- attributes: attrs,
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
cs_partial_cmp(cx, span, substr, tag_then_data)
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 33fe98b40..07b172bc7 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -20,7 +20,6 @@ pub fn expand_deriving_default(
) {
item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
- let attrs = thin_vec![cx.attr_word(sym::inline, span)];
let trait_def = TraitDef {
span,
path: Path::new(vec![kw::Default, sym::Default]),
@@ -34,7 +33,7 @@ pub fn expand_deriving_default(
explicit_self: false,
nonself_args: Vec::new(),
ret_ty: Self_,
- attributes: attrs,
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
match substr.fields {
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs
index 4eee573db..101401f9c 100644
--- a/compiler/rustc_builtin_macros/src/deriving/hash.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs
@@ -1,7 +1,7 @@
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::{path_std, pathvec_std};
-use rustc_ast::{AttrVec, MetaItem, Mutability};
+use rustc_ast::{MetaItem, Mutability};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
@@ -33,7 +33,7 @@ pub fn expand_deriving_hash(
explicit_self: true,
nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)],
ret_ty: Unit,
- attributes: AttrVec::new(),
+ attributes: thin_vec![cx.attr_word(sym::inline, span)],
fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
hash_substructure(a, b, c)
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index d0d786460..7b2a375a8 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -88,6 +88,83 @@ pub(crate) struct ConcatBytestr {
}
#[derive(Diagnostic)]
+#[diag(builtin_macros_concat_c_str_lit)]
+pub(crate) struct ConcatCStrLit {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_export_macro_rules)]
+pub(crate) struct ExportMacroRules {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_proc_macro)]
+pub(crate) struct ProcMacro {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_invalid_crate_attribute)]
+pub(crate) struct InvalidCrateAttr {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_non_abi)]
+pub(crate) struct NonABI {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_trace_macros)]
+pub(crate) struct TraceMacros {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_bench_sig)]
+pub(crate) struct BenchSig {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_arg_non_lifetime)]
+pub(crate) struct TestArgNonLifetime {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_should_panic)]
+pub(crate) struct ShouldPanic {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_test_args)]
+pub(crate) struct TestArgs {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_alloc_must_statics)]
+pub(crate) struct AllocMustStatics {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_invalid)]
pub(crate) struct ConcatBytesInvalid {
#[primary_span]
@@ -202,6 +279,10 @@ pub(crate) struct BadDeriveTarget {
}
#[derive(Diagnostic)]
+#[diag(builtin_macros_tests_not_support)]
+pub(crate) struct TestsNotSupport {}
+
+#[derive(Diagnostic)]
#[diag(builtin_macros_unexpected_lit, code = "E0777")]
pub(crate) struct BadDeriveLit {
#[primary_span]
@@ -377,7 +458,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
rustc::untranslatable_diagnostic,
reason = "cannot translate user-provided messages"
)]
- handler.struct_diagnostic(msg.as_str())
+ handler.struct_diagnostic(msg.to_string())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
};
@@ -732,3 +813,10 @@ pub(crate) struct TestRunnerNargs {
#[primary_span]
pub(crate) span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_expected_register_class_or_explicit_register)]
+pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
+ #[primary_span]
+ pub(crate) span: Span,
+}
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index c59a733c0..4c878785b 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -554,9 +554,6 @@ fn report_missing_placeholders(
fmt_span: Span,
) {
let mut diag = if let &[(span, named)] = &unused[..] {
- //let mut diag = ecx.struct_span_err(span, msg);
- //diag.span_label(span, msg);
- //diag
ecx.create_err(errors::FormatUnusedArg { span, named })
} else {
let unused_labels =
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index f0d378d12..577247193 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -1,5 +1,6 @@
use crate::util::check_builtin_macro_attribute;
+use crate::errors;
use rustc_ast::expand::allocator::{
global_fn_name, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
};
@@ -34,7 +35,7 @@ pub fn expand(
{
(item, true, ecx.with_def_site_ctxt(ty.span))
} else {
- ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
+ ecx.sess.parse_sess.span_diagnostic.emit_err(errors::AllocMustStatics{span: item.span()});
return vec![orig_item];
};
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 52b5601bb..b35a2e2a2 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -1,3 +1,4 @@
+use crate::errors;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{self as ast, attr, NodeId};
@@ -83,12 +84,7 @@ pub fn inject(
impl<'a> CollectProcMacros<'a> {
fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
- self.handler.span_err(
- sp,
- "`proc-macro` crate types currently cannot export any items other \
- than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, \
- or `#[proc_macro_attribute]`",
- );
+ self.handler.emit_err(errors::ProcMacro { span: sp });
}
}
@@ -157,9 +153,9 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
fn visit_item(&mut self, item: &'a ast::Item) {
if let ast::ItemKind::MacroDef(..) = item.kind {
if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
- let msg =
- "cannot export macro_rules! macros from a `proc-macro` crate type currently";
- self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
+ self.handler.emit_err(errors::ExportMacroRules {
+ span: self.source_map.guess_head_span(item.span),
+ });
}
}
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 49ee276af..6bc4f6fc1 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -3,12 +3,12 @@ use crate::errors;
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, attr};
+use rustc_ast::{self as ast, attr, GenericParamKind};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_expand::base::*;
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{FileNameDisplayPreference, Span};
+use rustc_span::{ErrorGuaranteed, FileNameDisplayPreference, Span};
use std::iter;
use thin_vec::{thin_vec, ThinVec};
@@ -122,23 +122,26 @@ pub fn expand_test_or_bench(
let ast::ItemKind::Fn(fn_) = &item.kind else {
not_testable_error(cx, attr_sp, Some(&item));
return if is_stmt {
- vec![Annotatable::Stmt(P(ast::Stmt {
- id: ast::DUMMY_NODE_ID,
- span: item.span,
- kind: ast::StmtKind::Item(item),
- }))]
+ vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
} else {
vec![Annotatable::Item(item)]
};
};
- // has_*_signature will report any errors in the type so compilation
+ // check_*_signature will report any errors in the type so compilation
// will fail. We shouldn't try to expand in this case because the errors
// would be spurious.
- if (!is_bench && !has_test_signature(cx, &item))
- || (is_bench && !has_bench_signature(cx, &item))
- {
- return vec![Annotatable::Item(item)];
+ let check_result = if is_bench {
+ check_bench_signature(cx, &item, &fn_)
+ } else {
+ check_test_signature(cx, &item, &fn_)
+ };
+ if check_result.is_err() {
+ return if is_stmt {
+ vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
+ } else {
+ vec![Annotatable::Item(item)]
+ };
}
let sp = cx.with_def_site_ctxt(item.span);
@@ -523,75 +526,57 @@ fn test_type(cx: &ExtCtxt<'_>) -> TestType {
}
}
-fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
+fn check_test_signature(
+ cx: &ExtCtxt<'_>,
+ i: &ast::Item,
+ f: &ast::Fn,
+) -> Result<(), ErrorGuaranteed> {
let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
let sd = &cx.sess.parse_sess.span_diagnostic;
- match &i.kind {
- ast::ItemKind::Fn(box ast::Fn { sig, generics, .. }) => {
- if let ast::Unsafe::Yes(span) = sig.header.unsafety {
- sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" });
- return false;
- }
- if let ast::Async::Yes { span, .. } = sig.header.asyncness {
- sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" });
- return false;
- }
- // If the termination trait is active, the compiler will check that the output
- // type implements the `Termination` trait as `libtest` enforces that.
- let has_output = match &sig.decl.output {
- ast::FnRetTy::Default(..) => false,
- ast::FnRetTy::Ty(t) if t.kind.is_unit() => false,
- _ => true,
- };
-
- if !sig.decl.inputs.is_empty() {
- sd.span_err(i.span, "functions used as tests can not have any arguments");
- return false;
- }
+ if let ast::Unsafe::Yes(span) = f.sig.header.unsafety {
+ return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
+ }
- match (has_output, has_should_panic_attr) {
- (true, true) => {
- sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
- false
- }
- (true, false) => {
- if !generics.params.is_empty() {
- sd.span_err(
- i.span,
- "functions used as tests must have signature fn() -> ()",
- );
- false
- } else {
- true
- }
- }
- (false, _) => true,
- }
- }
- _ => {
- // should be unreachable because `is_test_fn_item` should catch all non-fn items
- debug_assert!(false);
- false
- }
+ if let ast::Async::Yes { span, .. } = f.sig.header.asyncness {
+ return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" }));
}
-}
-fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
- let has_sig = match &i.kind {
- // N.B., inadequate check, but we're running
- // well before resolve, can't get too deep.
- ast::ItemKind::Fn(box ast::Fn { sig, .. }) => sig.decl.inputs.len() == 1,
- _ => false,
+ // If the termination trait is active, the compiler will check that the output
+ // type implements the `Termination` trait as `libtest` enforces that.
+ let has_output = match &f.sig.decl.output {
+ ast::FnRetTy::Default(..) => false,
+ ast::FnRetTy::Ty(t) if t.kind.is_unit() => false,
+ _ => true,
};
- if !has_sig {
- cx.sess.parse_sess.span_diagnostic.span_err(
+ if !f.sig.decl.inputs.is_empty() {
+ return Err(sd.span_err(i.span, "functions used as tests can not have any arguments"));
+ }
+
+ if has_should_panic_attr && has_output {
+ return Err(sd.span_err(i.span, "functions using `#[should_panic]` must return `()`"));
+ }
+
+ if f.generics.params.iter().any(|param| !matches!(param.kind, GenericParamKind::Lifetime)) {
+ return Err(sd.span_err(
i.span,
- "functions used as benches must have \
- signature `fn(&mut Bencher) -> impl Termination`",
- );
+ "functions used as tests can not have any non-lifetime generic parameters",
+ ));
}
- has_sig
+ Ok(())
+}
+
+fn check_bench_signature(
+ cx: &ExtCtxt<'_>,
+ i: &ast::Item,
+ f: &ast::Fn,
+) -> Result<(), ErrorGuaranteed> {
+ // N.B., inadequate check, but we're running
+ // well before resolve, can't get too deep.
+ if f.sig.decl.inputs.len() != 1 {
+ return Err(cx.sess.parse_sess.span_diagnostic.emit_err(errors::BenchSig { span: i.span }));
+ }
+ Ok(())
}
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 9bc1e27b4..81b618548 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -63,10 +63,7 @@ pub fn inject(krate: &mut ast::Crate, sess: &Session, resolver: &mut dyn Resolve
// Silently allow compiling with panic=abort on these platforms,
// but with old behavior (abort if a test fails).
} else {
- span_diagnostic.err(
- "building tests with panic=abort is not supported \
- without `-Zpanic_abort_tests`",
- );
+ span_diagnostic.emit_err(errors::TestsNotSupport {});
}
PanicStrategy::Unwind
}
diff --git a/compiler/rustc_builtin_macros/src/trace_macros.rs b/compiler/rustc_builtin_macros/src/trace_macros.rs
index 9c98723e1..af1a392ac 100644
--- a/compiler/rustc_builtin_macros/src/trace_macros.rs
+++ b/compiler/rustc_builtin_macros/src/trace_macros.rs
@@ -1,3 +1,4 @@
+use crate::errors;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_expand::base::{self, ExtCtxt};
use rustc_span::symbol::kw;
@@ -20,7 +21,7 @@ pub fn expand_trace_macros(
};
err |= cursor.next().is_some();
if err {
- cx.span_err(sp, "trace_macros! accepts only `true` or `false`")
+ cx.emit_err(errors::TraceMacros { span: sp });
} else {
cx.set_trace_macros(value);
}
diff --git a/compiler/rustc_codegen_cranelift/.cirrus.yml b/compiler/rustc_codegen_cranelift/.cirrus.yml
index 7886cae42..8b4efd4e3 100644
--- a/compiler/rustc_codegen_cranelift/.cirrus.yml
+++ b/compiler/rustc_codegen_cranelift/.cirrus.yml
@@ -1,16 +1,16 @@
task:
name: freebsd
freebsd_instance:
- image: freebsd-13-1-release-amd64
+ image: freebsd-13-2-release-amd64
setup_rust_script:
- - pkg install -y curl git bash
+ - pkg install -y git bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh --default-toolchain none -y --profile=minimal
target_cache:
folder: target
prepare_script:
- . $HOME/.cargo/env
- - ./y.rs prepare
+ - ./y.sh prepare
test_script:
- . $HOME/.cargo/env
- - ./y.rs test
+ - ./y.sh test
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
index 3c4055566..12aa69d3c 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
@@ -46,12 +46,12 @@ jobs:
run: rustup set default-host x86_64-pc-windows-gnu
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
- name: Build
- run: ./y.rs build --sysroot none
+ run: ./y.sh build --sysroot none
- name: Test abi-cafe
env:
TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
- run: ./y.rs abi-cafe
+ run: ./y.sh abi-cafe
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
index e4af73ea6..8e6c1e8ad 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
@@ -19,7 +19,7 @@ jobs:
- name: Rustfmt
run: |
cargo fmt --check
- rustfmt --check build_system/mod.rs
+ rustfmt --check build_system/main.rs
rustfmt --check example/*
@@ -91,22 +91,52 @@ jobs:
sudo apt-get install -y gcc-s390x-linux-gnu qemu-user
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
+
+ - name: Build
+ run: ./y.sh build --sysroot none
- - name: Build without unstable features
+ - name: Test
env:
TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
- # This is the config rust-lang/rust uses for builds
- run: ./y.rs build --no-unstable-features
+ run: ./y.sh test
- - name: Build
- run: ./y.rs build --sysroot none
+ - name: Install LLVM standard library
+ run: rustup target add ${{ matrix.env.TARGET_TRIPLE }}
- - name: Test
+ # This is roughly config rust-lang/rust uses for testing
+ - name: Test with LLVM sysroot
+ # Skip native x86_64-pc-windows-gnu. It is way too slow and cross-compiled
+ # x86_64-pc-windows-gnu covers at least part of the tests.
+ if: matrix.os != 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu'
env:
TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
- run: ./y.rs test
+ run: ./y.sh test --sysroot llvm --no-unstable-features
+
+
+ # This job doesn't use cg_clif in any way. It checks that all cg_clif tests work with cg_llvm too.
+ test_llvm:
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+
+ defaults:
+ run:
+ shell: bash
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Prepare dependencies
+ run: ./y.rs prepare
+
+ - name: Disable JIT tests
+ run: |
+ sed -i 's/jit./#jit./' config.txt
+
+ - name: Test
+ env:
+ TARGET_TRIPLE: x86_64-unknown-linux-gnu
+ run: ./y.rs test --use-backend llvm
bench:
runs-on: ubuntu-latest
@@ -135,13 +165,13 @@ jobs:
run: cargo install hyperfine || true
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
- name: Build
- run: CI_OPT=1 ./y.rs build --sysroot none
+ run: CI_OPT=1 ./y.sh build --sysroot none
- name: Benchmark
- run: CI_OPT=1 ./y.rs bench
+ run: CI_OPT=1 ./y.sh bench
dist:
@@ -194,13 +224,13 @@ jobs:
sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
- name: Build backend
- run: CI_OPT=1 ./y.rs build --sysroot none
+ run: CI_OPT=1 ./y.sh build --sysroot none
- name: Build sysroot
- run: CI_OPT=1 ./y.rs build
+ run: CI_OPT=1 ./y.sh build
- name: Package prebuilt cg_clif
run: tar cvfJ cg_clif.tar.xz dist
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
index b2f772c4f..b49dc3aff 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/rustc.yml
@@ -18,7 +18,7 @@ jobs:
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
- name: Test
run: ./scripts/test_bootstrap.sh
@@ -38,7 +38,7 @@ jobs:
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- name: Prepare dependencies
- run: ./y.rs prepare
+ run: ./y.sh prepare
- name: Test
run: ./scripts/test_rustc_tests.sh
diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json
index 7c8703cba..60cb51d56 100644
--- a/compiler/rustc_codegen_cranelift/.vscode/settings.json
+++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json
@@ -6,9 +6,10 @@
"rust-analyzer.imports.granularity.enforce": true,
"rust-analyzer.imports.granularity.group": "module",
"rust-analyzer.imports.prefix": "crate",
- "rust-analyzer.cargo.features": ["unstable-features", "__check_build_system_using_ra"],
+ "rust-analyzer.cargo.features": ["unstable-features"],
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
+ "./build_system/Cargo.toml",
{
"crates": [
{
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index 07a8e431a..904233d42 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -20,6 +20,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
+name = "arbitrary"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e"
+
+[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -38,12 +44,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -51,23 +51,24 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cranelift-bforest"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70"
+checksum = "9b6160c0a96253993b79fb7e0983534a4515ecf666120ddf8f92068114997ebc"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220"
+checksum = "7b38da5f63562e42f3c929d7c76871098e5ad12c8ab44b0659ffc529f22a5b3a"
dependencies = [
"bumpalo",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
+ "cranelift-control",
"cranelift-entity",
"cranelift-isle",
"gimli",
@@ -80,30 +81,39 @@ dependencies = [
[[package]]
name = "cranelift-codegen-meta"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da"
+checksum = "011371e213e163b55dd9e8404b3f2d9fa52cd14dc2f3dc5b83e61ffceff126db"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8"
+checksum = "1bf97dde7f5ad571161cdd203a2c9c88682ef669830aea3c14ea5d164ef8bb43"
+
+[[package]]
+name = "cranelift-control"
+version = "0.96.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd9a9254aee733b0f2b68e5eaaf0337ad53cb23252a056c10a35370551be8d40"
+dependencies = [
+ "arbitrary",
+]
[[package]]
name = "cranelift-entity"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0"
+checksum = "baf39a33ee39479d1337cd9333f3c09786c5a0ca1ec509edcaf9d1346d5de0e5"
[[package]]
name = "cranelift-frontend"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d"
+checksum = "65e260b92a193a0a2dccc3938f133d9532e7dcfe8d03e36bf8b7d3518c1c1793"
dependencies = [
"cranelift-codegen",
"log",
@@ -113,18 +123,19 @@ dependencies = [
[[package]]
name = "cranelift-isle"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba"
+checksum = "9446c8e1aadfcdacee1a49592bc2c25d1d9bf5484782c163e7f5485c92cd3c1c"
[[package]]
name = "cranelift-jit"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ca96b05988aa057eda09a817a6e31915fabd7f476b513123aff08053cd193dd"
+checksum = "689a6df165d0f860c1e1a3d53c28944e2743c3e9ee4c678cf190fe60ad7a6ef5"
dependencies = [
"anyhow",
"cranelift-codegen",
+ "cranelift-control",
"cranelift-entity",
"cranelift-module",
"cranelift-native",
@@ -138,19 +149,20 @@ dependencies = [
[[package]]
name = "cranelift-module"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5112c0be9cc5da064e0620570d67852f11ce44f2e572a58ecf7f11df73978b8"
+checksum = "0b1402d6ff1695b429536b2eaa126db560fc94c375ed0e9cfb15051fc07427f7"
dependencies = [
"anyhow",
"cranelift-codegen",
+ "cranelift-control",
]
[[package]]
name = "cranelift-native"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00"
+checksum = "eac916f3c5aff4b817e42fc2e682292b931495b3fe2603d5e3c3cf602d74e344"
dependencies = [
"cranelift-codegen",
"libc",
@@ -159,12 +171,13 @@ dependencies = [
[[package]]
name = "cranelift-object"
-version = "0.95.1"
+version = "0.96.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48ed1b37d0972abe804cb5bf2b35f3a76a276ebbe148e3a726d8e31042790978"
+checksum = "23860f4cd064017f2108e6bc5d25660a77cd6eea77f1ac0756870a00abb12e93"
dependencies = [
"anyhow",
"cranelift-codegen",
+ "cranelift-control",
"cranelift-module",
"log",
"object",
@@ -187,15 +200,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
name = "gimli"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -273,9 +277,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "object"
-version = "0.30.3"
+version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
dependencies = [
"crc32fast",
"hashbrown 0.13.2",
@@ -291,12 +295,13 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "regalloc2"
-version = "0.6.1"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621"
+checksum = "d4a52e724646c6c0800fc456ec43b4165d2f91fba88ceaca06d9e0b400023478"
dependencies = [
- "fxhash",
+ "hashbrown 0.13.2",
"log",
+ "rustc-hash",
"slice-group-by",
"smallvec",
]
@@ -314,6 +319,12 @@ dependencies = [
]
[[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 = [
@@ -327,7 +338,6 @@ dependencies = [
"indexmap",
"libloading",
"object",
- "once_cell",
"smallvec",
"target-lexicon",
]
@@ -364,9 +374,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasmtime-jit-icache-coherence"
-version = "8.0.1"
+version = "9.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd"
+checksum = "7d90933b781e1cef7656baed671c7a90bdba0c1c694e04fdd4124419308f5cbb"
dependencies = [
"cfg-if",
"libc",
@@ -397,18 +407,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
-version = "0.45.0"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
@@ -421,42 +431,42 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.1"
+version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index a2890f6dd..1c1f2d857 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -3,31 +3,23 @@ name = "rustc_codegen_cranelift"
version = "0.1.0"
edition = "2021"
-[[bin]]
-# This is used just to teach rust-analyzer how to check the build system. required-features is used
-# to disable it for regular builds.
-name = "y"
-path = "./y.rs"
-required-features = ["__check_build_system_using_ra"]
-
[lib]
crate-type = ["dylib"]
[dependencies]
# These have to be in sync with each other
-cranelift-codegen = { version = "0.95.1", features = ["unwind", "all-arch"] }
-cranelift-frontend = { version = "0.95.1" }
-cranelift-module = { version = "0.95.1" }
-cranelift-native = { version = "0.95.1" }
-cranelift-jit = { version = "0.95.1", optional = true }
-cranelift-object = { version = "0.95.1" }
+cranelift-codegen = { version = "0.96.1", features = ["unwind", "all-arch"] }
+cranelift-frontend = { version = "0.96.1" }
+cranelift-module = { version = "0.96.1" }
+cranelift-native = { version = "0.96.1" }
+cranelift-jit = { version = "0.96.1", optional = true }
+cranelift-object = { version = "0.96.1" }
target-lexicon = "0.12.0"
gimli = { version = "0.27.2", default-features = false, features = ["write"]}
object = { version = "0.30.3", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
indexmap = "1.9.3"
libloading = { version = "0.7.3", optional = true }
-once_cell = "1.10.0"
smallvec = "1.8.1"
[patch.crates-io]
@@ -46,7 +38,6 @@ smallvec = "1.8.1"
unstable-features = ["jit", "inline_asm"]
jit = ["cranelift-jit", "libloading"]
inline_asm = []
-__check_build_system_using_ra = []
[package.metadata.rust-analyzer]
rustc_private = true
diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md
index c5222982a..9469feea0 100644
--- a/compiler/rustc_codegen_cranelift/Readme.md
+++ b/compiler/rustc_codegen_cranelift/Readme.md
@@ -10,8 +10,8 @@ If not please open an issue.
```bash
$ git clone https://github.com/bjorn3/rustc_codegen_cranelift
$ cd rustc_codegen_cranelift
-$ ./y.rs prepare
-$ ./y.rs build
+$ ./y.sh prepare
+$ ./y.sh build
```
To run the test suite replace the last command with:
@@ -20,9 +20,14 @@ To run the test suite replace the last command with:
$ ./test.sh
```
-For more docs on how to build and test see [build_system/usage.txt](build_system/usage.txt) or the help message of `./y.rs`.
+For more docs on how to build and test see [build_system/usage.txt](build_system/usage.txt) or the help message of `./y.sh`.
+
+## Precompiled builds
Alternatively you can download a pre built version from the [releases] page.
+Extract the `dist` directory in the archive anywhere you want.
+If you want to use `cargo clif build` instead of having to specify the full path to the `cargo-clif` executable, you can add the `bin` subdirectory of the extracted `dist` directory to your `PATH`.
+(tutorial [for Windows](https://stackoverflow.com/a/44272417), and [for Linux/MacOS](https://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path/26059#26059)).
[releases]: https://github.com/bjorn3/rustc_codegen_cranelift/releases/tag/dev
@@ -30,7 +35,7 @@ Alternatively you can download a pre built version from the [releases] page.
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 (`y.rs prepare` and `y.rs build` or `test.sh`).
+Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`y.sh prepare` and `y.sh build` or `test.sh`).
In the directory with your project (where you can do the usual `cargo build`), run:
@@ -42,6 +47,32 @@ This will build your project with rustc_codegen_cranelift instead of the usual L
For additional ways to use rustc_codegen_cranelift like the JIT mode see [usage.md](docs/usage.md).
+## Building and testing with changes in rustc code
+
+This is useful when changing code in `rustc_codegen_cranelift` as part of changing [main Rust repository](https://github.com/rust-lang/rust/).
+This can happen, for example, when you are implementing a new compiler intrinsic.
+
+Instruction below uses `$RustCheckoutDir` as substitute for any folder where you cloned Rust repository.
+
+You need to do this steps to successfully compile and use the cranelift backend with your changes in rustc code:
+
+1. `cd $RustCheckoutDir`
+2. Run `python x.py setup` and choose option for compiler (`b`).
+3. Build compiler and necessary tools: `python x.py build --stage=2 compiler library/std src/tools/rustdoc src/tools/rustfmt`
+ * (Optional) You can also build cargo by adding `src/tools/cargo` to previous command.
+4. Copy exectutable files from `./build/host/stage2-tools/<your hostname triple>/release`
+to `./build/host/stage2/bin/`. Note that you would need to do this every time you rebuilt `rust` repository.
+5. Copy cargo from another toolchain: `cp $(rustup which cargo) .build/<your hostname triple>/stage2/bin/cargo`
+ * Another option is to build it at step 3 and copy with other executables at step 4.
+6. Link your new `rustc` to toolchain: `rustup toolchain link stage2 ./build/host/stage2/`.
+7. (Windows only) compile y.rs: `rustc +stage2 -O y.rs`.
+8. You need to prefix every `./y.rs` (or `y` if you built `y.rs`) command by `rustup run stage2` to make cg_clif use your local changes in rustc.
+
+ * `rustup run stage2 ./y.rs prepare`
+ * `rustup run stage2 ./y.rs build`
+ * (Optional) run tests: `rustup run stage2 ./y.rs test`
+9. Now you can use your cg_clif build to compile other Rust programs, e.g. you can open any Rust crate and run commands like `$RustCheckoutDir/compiler/rustc_codegen_cranelift/dist/cargo-clif build --release`.
+
## Configuration
See the documentation on the `BackendConfig` struct in [config.rs](src/config.rs) for all
diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml
deleted file mode 100644
index 8219e6b6c..000000000
--- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml
+++ /dev/null
@@ -1,35 +0,0 @@
-[package]
-name = "sysroot"
-version = "0.0.0"
-
-[dependencies]
-core = { path = "./sysroot_src/library/core" }
-alloc = { path = "./sysroot_src/library/alloc" }
-std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] }
-test = { path = "./sysroot_src/library/test" }
-
-compiler_builtins = { version = "0.1.87", default-features = false, features = ["no-asm"] }
-
-[patch.crates-io]
-rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" }
-rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" }
-rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-std" }
-
-[profile.dev]
-lto = "off"
-
-[profile.release]
-debug = true
-incremental = true
-lto = "off"
-
-# Mandatory for correctly compiling compiler-builtins
-[profile.dev.package.compiler_builtins]
-debug-assertions = false
-overflow-checks = false
-codegen-units = 10000
-
-[profile.release.package.compiler_builtins]
-debug-assertions = false
-overflow-checks = false
-codegen-units = 10000
diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs b/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs
deleted file mode 100644
index 0c9ac1ac8..000000000
--- a/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-#![no_std]
diff --git a/compiler/rustc_codegen_cranelift/build_system/Cargo.lock b/compiler/rustc_codegen_cranelift/build_system/Cargo.lock
new file mode 100644
index 000000000..86268e191
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/build_system/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "y"
+version = "0.1.0"
diff --git a/compiler/rustc_codegen_cranelift/build_system/Cargo.toml b/compiler/rustc_codegen_cranelift/build_system/Cargo.toml
new file mode 100644
index 000000000..f47b9bc55
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/build_system/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "y"
+version = "0.1.0"
+edition = "2021"
+
+[[bin]]
+name = "y"
+path = "main.rs"
+
+[features]
+unstable-features = [] # for rust-analyzer
+
+# Do not add any dependencies
diff --git a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
index 0da27f529..29c127bf5 100644
--- a/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/abi_cafe.rs
@@ -1,25 +1,29 @@
-use std::path::Path;
-
use super::build_sysroot;
use super::path::Dirs;
use super::prepare::GitRepo;
use super::utils::{spawn_and_wait, CargoProject, Compiler};
-use super::SysrootKind;
+use super::{CodegenBackend, SysrootKind};
-static ABI_CAFE_REPO: GitRepo =
- GitRepo::github("Gankra", "abi-cafe", "4c6dc8c9c687e2b3a760ff2176ce236872b37212", "abi-cafe");
+static ABI_CAFE_REPO: GitRepo = GitRepo::github(
+ "Gankra",
+ "abi-cafe",
+ "4c6dc8c9c687e2b3a760ff2176ce236872b37212",
+ "588df6d66abbe105",
+ "abi-cafe",
+);
-static ABI_CAFE: CargoProject = CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe");
+static ABI_CAFE: CargoProject = CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe_target");
pub(crate) fn run(
channel: &str,
sysroot_kind: SysrootKind,
dirs: &Dirs,
- cg_clif_dylib: &Path,
+ cg_clif_dylib: &CodegenBackend,
+ rustup_toolchain_name: Option<&str>,
bootstrap_host_compiler: &Compiler,
) {
ABI_CAFE_REPO.fetch(dirs);
- spawn_and_wait(ABI_CAFE.fetch("cargo", &bootstrap_host_compiler.rustc, dirs));
+ ABI_CAFE_REPO.patch(dirs);
eprintln!("Building sysroot for abi-cafe");
build_sysroot::build_sysroot(
@@ -28,6 +32,7 @@ pub(crate) fn run(
sysroot_kind,
cg_clif_dylib,
bootstrap_host_compiler,
+ rustup_toolchain_name,
bootstrap_host_compiler.triple.clone(),
);
@@ -40,7 +45,14 @@ pub(crate) fn run(
cmd.arg("--pairs");
cmd.args(pairs);
cmd.arg("--add-rustc-codegen-backend");
- cmd.arg(format!("cgclif:{}", cg_clif_dylib.display()));
+ match cg_clif_dylib {
+ CodegenBackend::Local(path) => {
+ cmd.arg(format!("cgclif:{}", path.display()));
+ }
+ CodegenBackend::Builtin(name) => {
+ cmd.arg(format!("cgclif:{name}"));
+ }
+ }
cmd.current_dir(ABI_CAFE.source_dir(dirs));
spawn_and_wait(cmd);
diff --git a/compiler/rustc_codegen_cranelift/build_system/bench.rs b/compiler/rustc_codegen_cranelift/build_system/bench.rs
index a9a851d0a..2bb118000 100644
--- a/compiler/rustc_codegen_cranelift/build_system/bench.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/bench.rs
@@ -1,26 +1,19 @@
use std::env;
-use std::fs;
use std::path::Path;
use super::path::{Dirs, RelPath};
use super::prepare::GitRepo;
use super::rustc_info::get_file_name;
-use super::utils::{hyperfine_command, spawn_and_wait, CargoProject, Compiler};
+use super::utils::{hyperfine_command, spawn_and_wait, Compiler};
static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github(
"ebobby",
"simple-raytracer",
"804a7a21b9e673a482797aa289a18ed480e4d813",
+ "ad6f59a2331a3f56",
"<none>",
);
-// Use a separate target dir for the initial LLVM build to reduce unnecessary recompiles
-static SIMPLE_RAYTRACER_LLVM: CargoProject =
- CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer_llvm");
-
-static SIMPLE_RAYTRACER: CargoProject =
- CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer");
-
pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
benchmark_simple_raytracer(dirs, bootstrap_host_compiler);
}
@@ -32,35 +25,17 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
std::process::exit(1);
}
- if !SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).exists() {
- SIMPLE_RAYTRACER_REPO.fetch(dirs);
- spawn_and_wait(SIMPLE_RAYTRACER.fetch(
- &bootstrap_host_compiler.cargo,
- &bootstrap_host_compiler.rustc,
- dirs,
- ));
- }
-
- eprintln!("[LLVM BUILD] simple-raytracer");
- let build_cmd = SIMPLE_RAYTRACER_LLVM.build(bootstrap_host_compiler, dirs);
- spawn_and_wait(build_cmd);
- fs::copy(
- SIMPLE_RAYTRACER_LLVM
- .target_dir(dirs)
- .join(&bootstrap_host_compiler.triple)
- .join("debug")
- .join(get_file_name("main", "bin")),
- RelPath::BUILD.to_path(dirs).join(get_file_name("raytracer_cg_llvm", "bin")),
- )
- .unwrap();
+ SIMPLE_RAYTRACER_REPO.fetch(dirs);
+ SIMPLE_RAYTRACER_REPO.patch(dirs);
let bench_runs = env::var("BENCH_RUNS").unwrap_or_else(|_| "10".to_string()).parse().unwrap();
eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
- let cargo_clif =
- RelPath::DIST.to_path(dirs).join(get_file_name("cargo_clif", "bin").replace('_', "-"));
- let manifest_path = SIMPLE_RAYTRACER.manifest_path(dirs);
- let target_dir = SIMPLE_RAYTRACER.target_dir(dirs);
+ let cargo_clif = RelPath::DIST
+ .to_path(dirs)
+ .join(get_file_name(&bootstrap_host_compiler.rustc, "cargo_clif", "bin").replace('_', "-"));
+ let manifest_path = SIMPLE_RAYTRACER_REPO.source_dir().to_path(dirs).join("Cargo.toml");
+ let target_dir = RelPath::BUILD.join("simple_raytracer").to_path(dirs);
let clean_cmd = format!(
"RUSTC=rustc cargo clean --manifest-path {manifest_path} --target-dir {target_dir}",
@@ -68,35 +43,52 @@ fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
target_dir = target_dir.display(),
);
let llvm_build_cmd = format!(
- "RUSTC=rustc cargo build --manifest-path {manifest_path} --target-dir {target_dir}",
+ "RUSTC=rustc cargo build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_llvm || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_llvm",
manifest_path = manifest_path.display(),
target_dir = target_dir.display(),
);
let clif_build_cmd = format!(
- "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir}",
+ "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} && (rm build/raytracer_cg_clif || true) && ln build/simple_raytracer/debug/main build/raytracer_cg_clif",
+ cargo_clif = cargo_clif.display(),
+ manifest_path = manifest_path.display(),
+ target_dir = target_dir.display(),
+ );
+ let clif_build_opt_cmd = format!(
+ "RUSTC=rustc {cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir} --release && (rm build/raytracer_cg_clif_opt || true) && ln build/simple_raytracer/release/main build/raytracer_cg_clif_opt",
cargo_clif = cargo_clif.display(),
manifest_path = manifest_path.display(),
target_dir = target_dir.display(),
);
- let bench_compile =
- hyperfine_command(1, bench_runs, Some(&clean_cmd), &llvm_build_cmd, &clif_build_cmd);
+ let bench_compile = hyperfine_command(
+ 1,
+ bench_runs,
+ Some(&clean_cmd),
+ &[&llvm_build_cmd, &clif_build_cmd, &clif_build_opt_cmd],
+ );
spawn_and_wait(bench_compile);
eprintln!("[BENCH RUN] ebobby/simple-raytracer");
- fs::copy(
- target_dir.join("debug").join(get_file_name("main", "bin")),
- RelPath::BUILD.to_path(dirs).join(get_file_name("raytracer_cg_clif", "bin")),
- )
- .unwrap();
let mut bench_run = hyperfine_command(
0,
bench_runs,
None,
- Path::new(".").join(get_file_name("raytracer_cg_llvm", "bin")).to_str().unwrap(),
- Path::new(".").join(get_file_name("raytracer_cg_clif", "bin")).to_str().unwrap(),
+ &[
+ Path::new(".")
+ .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_llvm", "bin"))
+ .to_str()
+ .unwrap(),
+ Path::new(".")
+ .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif", "bin"))
+ .to_str()
+ .unwrap(),
+ Path::new(".")
+ .join(get_file_name(&bootstrap_host_compiler.rustc, "raytracer_cg_clif_opt", "bin"))
+ .to_str()
+ .unwrap(),
+ ],
);
bench_run.current_dir(RelPath::BUILD.to_path(dirs));
spawn_and_wait(bench_run);
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
index 4b740fa2d..6855c1a7f 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_backend.rs
@@ -3,7 +3,7 @@ use std::path::PathBuf;
use super::path::{Dirs, RelPath};
use super::rustc_info::get_file_name;
-use super::utils::{is_ci, is_ci_opt, CargoProject, Compiler};
+use super::utils::{is_ci, is_ci_opt, maybe_incremental, CargoProject, Compiler};
pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
@@ -14,8 +14,7 @@ pub(crate) fn build_backend(
use_unstable_features: bool,
) -> PathBuf {
let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs);
-
- cmd.env("CARGO_BUILD_INCREMENTAL", "true"); // Force incr comp even in release mode
+ maybe_incremental(&mut cmd);
let mut rustflags = env::var("RUSTFLAGS").unwrap_or_default();
@@ -23,11 +22,9 @@ pub(crate) fn build_backend(
// Deny warnings on CI
rustflags += " -Dwarnings";
- // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
- cmd.env("CARGO_BUILD_INCREMENTAL", "false");
-
if !is_ci_opt() {
cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true");
+ cmd.env("CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS", "true");
}
}
@@ -52,5 +49,5 @@ pub(crate) fn build_backend(
.target_dir(dirs)
.join(&bootstrap_host_compiler.triple)
.join(channel)
- .join(get_file_name("rustc_codegen_cranelift", "dylib"))
+ .join(get_file_name(&bootstrap_host_compiler.rustc, "rustc_codegen_cranelift", "dylib"))
}
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
index 76b602fe7..74bba9ed5 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
@@ -1,11 +1,13 @@
use std::fs;
use std::path::{Path, PathBuf};
-use std::process::{self, Command};
+use std::process::Command;
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_file_name, get_rustc_version, get_toolchain_name};
-use super::utils::{remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler};
-use super::SysrootKind;
+use super::rustc_info::get_file_name;
+use super::utils::{
+ maybe_incremental, remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler,
+};
+use super::{CodegenBackend, SysrootKind};
static DIST_DIR: RelPath = RelPath::DIST;
static BIN_DIR: RelPath = RelPath::DIST.join("bin");
@@ -15,8 +17,9 @@ pub(crate) fn build_sysroot(
dirs: &Dirs,
channel: &str,
sysroot_kind: SysrootKind,
- cg_clif_dylib_src: &Path,
+ cg_clif_dylib_src: &CodegenBackend,
bootstrap_host_compiler: &Compiler,
+ rustup_toolchain_name: Option<&str>,
target_triple: String,
) -> Compiler {
eprintln!("[BUILD] sysroot {:?}", sysroot_kind);
@@ -27,32 +30,52 @@ pub(crate) fn build_sysroot(
let is_native = bootstrap_host_compiler.triple == target_triple;
- // Copy the backend
- let cg_clif_dylib_path = if cfg!(windows) {
- // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the
- // binaries.
- BIN_DIR
- } else {
- LIB_DIR
- }
- .to_path(dirs)
- .join(cg_clif_dylib_src.file_name().unwrap());
- try_hard_link(cg_clif_dylib_src, &cg_clif_dylib_path);
+ let cg_clif_dylib_path = match cg_clif_dylib_src {
+ CodegenBackend::Local(src_path) => {
+ // Copy the backend
+ let cg_clif_dylib_path = if cfg!(windows) {
+ // Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the
+ // binaries.
+ BIN_DIR
+ } else {
+ LIB_DIR
+ }
+ .to_path(dirs)
+ .join(src_path.file_name().unwrap());
+ try_hard_link(src_path, &cg_clif_dylib_path);
+ CodegenBackend::Local(cg_clif_dylib_path)
+ }
+ CodegenBackend::Builtin(name) => CodegenBackend::Builtin(name.clone()),
+ };
// Build and copy rustc and cargo wrappers
- let wrapper_base_name = get_file_name("____", "bin");
- let toolchain_name = get_toolchain_name();
+ let wrapper_base_name = get_file_name(&bootstrap_host_compiler.rustc, "____", "bin");
for wrapper in ["rustc-clif", "rustdoc-clif", "cargo-clif"] {
let wrapper_name = wrapper_base_name.replace("____", wrapper);
let mut build_cargo_wrapper_cmd = Command::new(&bootstrap_host_compiler.rustc);
let wrapper_path = DIST_DIR.to_path(dirs).join(&wrapper_name);
build_cargo_wrapper_cmd
- .env("TOOLCHAIN_NAME", toolchain_name.clone())
.arg(RelPath::SCRIPTS.to_path(dirs).join(&format!("{wrapper}.rs")))
.arg("-o")
.arg(&wrapper_path)
.arg("-Cstrip=debuginfo");
+ if let Some(rustup_toolchain_name) = &rustup_toolchain_name {
+ build_cargo_wrapper_cmd
+ .env("TOOLCHAIN_NAME", rustup_toolchain_name)
+ .env_remove("CARGO")
+ .env_remove("RUSTC")
+ .env_remove("RUSTDOC");
+ } else {
+ build_cargo_wrapper_cmd
+ .env_remove("TOOLCHAIN_NAME")
+ .env("CARGO", &bootstrap_host_compiler.cargo)
+ .env("RUSTC", &bootstrap_host_compiler.rustc)
+ .env("RUSTDOC", &bootstrap_host_compiler.rustdoc);
+ }
+ if let CodegenBackend::Builtin(name) = cg_clif_dylib_src {
+ build_cargo_wrapper_cmd.env("BUILTIN_BACKEND", name);
+ }
spawn_and_wait(build_cargo_wrapper_cmd);
try_hard_link(wrapper_path, BIN_DIR.to_path(dirs).join(wrapper_name));
}
@@ -134,12 +157,9 @@ impl SysrootTarget {
}
}
-pub(crate) static ORIG_BUILD_SYSROOT: RelPath = RelPath::SOURCE.join("build_sysroot");
-pub(crate) static BUILD_SYSROOT: RelPath = RelPath::DOWNLOAD.join("sysroot");
-pub(crate) static SYSROOT_RUSTC_VERSION: RelPath = BUILD_SYSROOT.join("rustc_version");
-pub(crate) static SYSROOT_SRC: RelPath = BUILD_SYSROOT.join("sysroot_src");
+pub(crate) static STDLIB_SRC: RelPath = RelPath::BUILD.join("stdlib");
pub(crate) static STANDARD_LIBRARY: CargoProject =
- CargoProject::new(&BUILD_SYSROOT, "build_sysroot");
+ CargoProject::new(&STDLIB_SRC.join("library/sysroot"), "stdlib_target");
pub(crate) static RTSTARTUP_SYSROOT: RelPath = RelPath::BUILD.join("rtstartup");
#[must_use]
@@ -147,7 +167,7 @@ fn build_sysroot_for_triple(
dirs: &Dirs,
channel: &str,
compiler: Compiler,
- cg_clif_dylib_path: &Path,
+ cg_clif_dylib_path: &CodegenBackend,
sysroot_kind: SysrootKind,
) -> SysrootTarget {
match sysroot_kind {
@@ -155,7 +175,7 @@ fn build_sysroot_for_triple(
.unwrap_or(SysrootTarget { triple: compiler.triple, libs: vec![] }),
SysrootKind::Llvm => build_llvm_sysroot_for_triple(compiler),
SysrootKind::Clif => {
- build_clif_sysroot_for_triple(dirs, channel, compiler, &cg_clif_dylib_path)
+ build_clif_sysroot_for_triple(dirs, channel, compiler, cg_clif_dylib_path)
}
}
}
@@ -199,26 +219,8 @@ fn build_clif_sysroot_for_triple(
dirs: &Dirs,
channel: &str,
mut compiler: Compiler,
- cg_clif_dylib_path: &Path,
+ cg_clif_dylib_path: &CodegenBackend,
) -> SysrootTarget {
- match fs::read_to_string(SYSROOT_RUSTC_VERSION.to_path(dirs)) {
- Err(e) => {
- eprintln!("Failed to get rustc version for patched sysroot source: {}", e);
- eprintln!("Hint: Try `./y.rs prepare` to patch the sysroot source");
- process::exit(1);
- }
- Ok(source_version) => {
- let rustc_version = get_rustc_version(&compiler.rustc);
- if source_version != rustc_version {
- eprintln!("The patched sysroot source is outdated");
- eprintln!("Source version: {}", source_version.trim());
- eprintln!("Rustc version: {}", rustc_version.trim());
- eprintln!("Hint: Try `./y.rs prepare` to update the patched sysroot source");
- process::exit(1);
- }
- }
- }
-
let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
if let Some(rtstartup_target_libs) = build_rtstartup(dirs, &compiler) {
@@ -237,19 +239,28 @@ fn build_clif_sysroot_for_triple(
// Build sysroot
let mut rustflags = " -Zforce-unstable-if-unmarked -Cpanic=abort".to_string();
- rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap()));
+ match cg_clif_dylib_path {
+ CodegenBackend::Local(path) => {
+ rustflags.push_str(&format!(" -Zcodegen-backend={}", path.to_str().unwrap()));
+ }
+ CodegenBackend::Builtin(name) => {
+ rustflags.push_str(&format!(" -Zcodegen-backend={name}"));
+ }
+ };
// Necessary for MinGW to find rsbegin.o and rsend.o
rustflags
- .push_str(&format!(" --sysroot={}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap()));
+ .push_str(&format!(" --sysroot {}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap()));
if channel == "release" {
rustflags.push_str(" -Zmir-opt-level=3");
}
compiler.rustflags += &rustflags;
let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
+ maybe_incremental(&mut build_cmd);
if channel == "release" {
build_cmd.arg("--release");
}
- build_cmd.arg("--locked");
+ build_cmd.arg("--features").arg("compiler-builtins-no-asm backtrace panic-unwind");
+ build_cmd.env("CARGO_PROFILE_RELEASE_DEBUG", "true");
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
if compiler.triple.contains("apple") {
build_cmd.env("CARGO_PROFILE_RELEASE_SPLIT_DEBUGINFO", "packed");
@@ -272,13 +283,17 @@ fn build_clif_sysroot_for_triple(
}
fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> {
+ if !super::config::get_bool("keep_sysroot") {
+ super::prepare::prepare_stdlib(dirs, &compiler.rustc);
+ }
+
if !compiler.triple.ends_with("windows-gnu") {
return None;
}
RTSTARTUP_SYSROOT.ensure_fresh(dirs);
- let rtstartup_src = SYSROOT_SRC.to_path(dirs).join("library").join("rtstartup");
+ let rtstartup_src = STDLIB_SRC.to_path(dirs).join("library").join("rtstartup");
let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
for file in ["rsbegin", "rsend"] {
diff --git a/compiler/rustc_codegen_cranelift/build_system/mod.rs b/compiler/rustc_codegen_cranelift/build_system/main.rs
index e4ed9be23..3bc78d5db 100644
--- a/compiler/rustc_codegen_cranelift/build_system/mod.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/main.rs
@@ -1,3 +1,7 @@
+#![warn(rust_2018_idioms)]
+#![warn(unused_lifetimes)]
+#![warn(unreachable_pub)]
+
use std::env;
use std::path::PathBuf;
use std::process;
@@ -37,13 +41,19 @@ enum Command {
}
#[derive(Copy, Clone, Debug)]
-pub(crate) enum SysrootKind {
+enum SysrootKind {
None,
Clif,
Llvm,
}
-pub(crate) fn main() {
+#[derive(Clone, Debug)]
+enum CodegenBackend {
+ Local(PathBuf),
+ Builtin(String),
+}
+
+fn main() {
if env::var("RUST_BACKTRACE").is_err() {
env::set_var("RUST_BACKTRACE", "1");
}
@@ -75,15 +85,24 @@ pub(crate) fn main() {
};
let mut out_dir = PathBuf::from(".");
+ let mut download_dir = None;
let mut channel = "release";
let mut sysroot_kind = SysrootKind::Clif;
let mut use_unstable_features = true;
+ let mut frozen = false;
+ let mut skip_tests = vec![];
+ let mut use_backend = None;
while let Some(arg) = args.next().as_deref() {
match arg {
"--out-dir" => {
out_dir = PathBuf::from(args.next().unwrap_or_else(|| {
arg_error!("--out-dir requires argument");
- }))
+ }));
+ }
+ "--download-dir" => {
+ download_dir = Some(PathBuf::from(args.next().unwrap_or_else(|| {
+ arg_error!("--download-dir requires argument");
+ })));
}
"--debug" => channel = "debug",
"--sysroot" => {
@@ -96,30 +115,79 @@ pub(crate) fn main() {
}
}
"--no-unstable-features" => use_unstable_features = false,
+ "--frozen" => frozen = true,
+ "--skip-test" => {
+ // FIXME check that all passed in tests actually exist
+ skip_tests.push(args.next().unwrap_or_else(|| {
+ arg_error!("--skip-test requires argument");
+ }));
+ }
+ "--use-backend" => {
+ use_backend = Some(match args.next() {
+ Some(name) => name,
+ None => arg_error!("--use-backend requires argument"),
+ });
+ }
flag if flag.starts_with("-") => arg_error!("Unknown flag {}", flag),
arg => arg_error!("Unexpected argument {}", arg),
}
}
- let bootstrap_host_compiler = Compiler::bootstrap_with_triple(
- std::env::var("HOST_TRIPLE")
+ let current_dir = std::env::current_dir().unwrap();
+ out_dir = current_dir.join(out_dir);
+
+ if command == Command::Prepare {
+ prepare::prepare(&path::Dirs {
+ source_dir: current_dir.clone(),
+ download_dir: download_dir
+ .map(|dir| current_dir.join(dir))
+ .unwrap_or_else(|| out_dir.join("download")),
+ build_dir: PathBuf::from("dummy_do_not_use"),
+ dist_dir: PathBuf::from("dummy_do_not_use"),
+ frozen,
+ });
+ process::exit(0);
+ }
+
+ let rustup_toolchain_name = match (env::var("CARGO"), env::var("RUSTC"), env::var("RUSTDOC")) {
+ (Ok(_), Ok(_), Ok(_)) => None,
+ (Err(_), Err(_), Err(_)) => Some(rustc_info::get_toolchain_name()),
+ _ => {
+ eprintln!("All of CARGO, RUSTC and RUSTDOC need to be set or none must be set");
+ process::exit(1);
+ }
+ };
+ let bootstrap_host_compiler = {
+ let cargo = rustc_info::get_cargo_path();
+ let rustc = rustc_info::get_rustc_path();
+ let rustdoc = rustc_info::get_rustdoc_path();
+ let triple = std::env::var("HOST_TRIPLE")
.ok()
.or_else(|| config::get_value("host"))
- .unwrap_or_else(|| rustc_info::get_host_triple()),
- );
+ .unwrap_or_else(|| rustc_info::get_host_triple(&rustc));
+ Compiler {
+ cargo,
+ rustc,
+ rustdoc,
+ rustflags: String::new(),
+ rustdocflags: String::new(),
+ triple,
+ runner: vec![],
+ }
+ };
let target_triple = std::env::var("TARGET_TRIPLE")
.ok()
.or_else(|| config::get_value("target"))
.unwrap_or_else(|| bootstrap_host_compiler.triple.clone());
- // FIXME allow changing the location of these dirs using cli arguments
- let current_dir = std::env::current_dir().unwrap();
- out_dir = current_dir.join(out_dir);
let dirs = path::Dirs {
source_dir: current_dir.clone(),
- download_dir: out_dir.join("download"),
+ download_dir: download_dir
+ .map(|dir| current_dir.join(dir))
+ .unwrap_or_else(|| out_dir.join("download")),
build_dir: out_dir.join("build"),
dist_dir: out_dir.join("dist"),
+ frozen,
};
path::RelPath::BUILD.ensure_exists(&dirs);
@@ -133,20 +201,19 @@ pub(crate) fn main() {
std::fs::File::create(target).unwrap();
}
- if command == Command::Prepare {
- prepare::prepare(&dirs);
- process::exit(0);
- }
-
env::set_var("RUSTC", "rustc_should_be_set_explicitly");
env::set_var("RUSTDOC", "rustdoc_should_be_set_explicitly");
- let cg_clif_dylib = build_backend::build_backend(
- &dirs,
- channel,
- &bootstrap_host_compiler,
- use_unstable_features,
- );
+ let cg_clif_dylib = if let Some(name) = use_backend {
+ CodegenBackend::Builtin(name)
+ } else {
+ CodegenBackend::Local(build_backend::build_backend(
+ &dirs,
+ channel,
+ &bootstrap_host_compiler,
+ use_unstable_features,
+ ))
+ };
match command {
Command::Prepare => {
// Handled above
@@ -156,8 +223,11 @@ pub(crate) fn main() {
&dirs,
channel,
sysroot_kind,
+ use_unstable_features,
+ &skip_tests.iter().map(|test| &**test).collect::<Vec<_>>(),
&cg_clif_dylib,
&bootstrap_host_compiler,
+ rustup_toolchain_name.as_deref(),
target_triple.clone(),
);
}
@@ -166,7 +236,14 @@ pub(crate) fn main() {
eprintln!("Abi-cafe doesn't support cross-compilation");
process::exit(1);
}
- abi_cafe::run(channel, sysroot_kind, &dirs, &cg_clif_dylib, &bootstrap_host_compiler);
+ abi_cafe::run(
+ channel,
+ sysroot_kind,
+ &dirs,
+ &cg_clif_dylib,
+ rustup_toolchain_name.as_deref(),
+ &bootstrap_host_compiler,
+ );
}
Command::Build => {
build_sysroot::build_sysroot(
@@ -175,6 +252,7 @@ pub(crate) fn main() {
sysroot_kind,
&cg_clif_dylib,
&bootstrap_host_compiler,
+ rustup_toolchain_name.as_deref(),
target_triple,
);
}
@@ -185,6 +263,7 @@ pub(crate) fn main() {
sysroot_kind,
&cg_clif_dylib,
&bootstrap_host_compiler,
+ rustup_toolchain_name.as_deref(),
target_triple,
);
bench::benchmark(&dirs, &bootstrap_host_compiler);
diff --git a/compiler/rustc_codegen_cranelift/build_system/path.rs b/compiler/rustc_codegen_cranelift/build_system/path.rs
index 329072300..4f86c0fd2 100644
--- a/compiler/rustc_codegen_cranelift/build_system/path.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/path.rs
@@ -9,6 +9,7 @@ pub(crate) struct Dirs {
pub(crate) download_dir: PathBuf,
pub(crate) build_dir: PathBuf,
pub(crate) dist_dir: PathBuf,
+ pub(crate) frozen: bool,
}
#[doc(hidden)]
diff --git a/compiler/rustc_codegen_cranelift/build_system/prepare.rs b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
index 6769e42d4..e31e39a48 100644
--- a/compiler/rustc_codegen_cranelift/build_system/prepare.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/prepare.rs
@@ -3,77 +3,60 @@ use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
-use super::build_sysroot::{BUILD_SYSROOT, ORIG_BUILD_SYSROOT, SYSROOT_RUSTC_VERSION, SYSROOT_SRC};
+use super::build_sysroot::STDLIB_SRC;
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_default_sysroot, get_rustc_version};
-use super::tests::LIBCORE_TESTS_SRC;
-use super::utils::{copy_dir_recursively, git_command, retry_spawn_and_wait, spawn_and_wait};
+use super::rustc_info::get_default_sysroot;
+use super::utils::{
+ copy_dir_recursively, git_command, remove_dir_if_exists, retry_spawn_and_wait, spawn_and_wait,
+};
pub(crate) fn prepare(dirs: &Dirs) {
- RelPath::DOWNLOAD.ensure_fresh(dirs);
-
- spawn_and_wait(super::build_backend::CG_CLIF.fetch("cargo", "rustc", dirs));
-
- prepare_stdlib(dirs);
- spawn_and_wait(super::build_sysroot::STANDARD_LIBRARY.fetch("cargo", "rustc", dirs));
-
- prepare_coretests(dirs);
- spawn_and_wait(super::tests::LIBCORE_TESTS.fetch("cargo", "rustc", dirs));
-
+ RelPath::DOWNLOAD.ensure_exists(dirs);
super::tests::RAND_REPO.fetch(dirs);
- spawn_and_wait(super::tests::RAND.fetch("cargo", "rustc", dirs));
super::tests::REGEX_REPO.fetch(dirs);
- spawn_and_wait(super::tests::REGEX.fetch("cargo", "rustc", dirs));
super::tests::PORTABLE_SIMD_REPO.fetch(dirs);
- spawn_and_wait(super::tests::PORTABLE_SIMD.fetch("cargo", "rustc", dirs));
}
-fn prepare_stdlib(dirs: &Dirs) {
- let sysroot_src_orig = get_default_sysroot(Path::new("rustc")).join("lib/rustlib/src/rust");
+pub(crate) fn prepare_stdlib(dirs: &Dirs, rustc: &Path) {
+ let sysroot_src_orig = get_default_sysroot(rustc).join("lib/rustlib/src/rust");
assert!(sysroot_src_orig.exists());
- eprintln!("[COPY] stdlib src");
-
- // FIXME ensure builds error out or update the copy if any of the files copied here change
- BUILD_SYSROOT.ensure_fresh(dirs);
- copy_dir_recursively(&ORIG_BUILD_SYSROOT.to_path(dirs), &BUILD_SYSROOT.to_path(dirs));
-
- fs::create_dir_all(SYSROOT_SRC.to_path(dirs).join("library")).unwrap();
- copy_dir_recursively(
- &sysroot_src_orig.join("library"),
- &SYSROOT_SRC.to_path(dirs).join("library"),
- );
-
- let rustc_version = get_rustc_version(Path::new("rustc"));
- fs::write(SYSROOT_RUSTC_VERSION.to_path(dirs), &rustc_version).unwrap();
-
- eprintln!("[GIT] init");
- init_git_repo(&SYSROOT_SRC.to_path(dirs));
-
- apply_patches(dirs, "stdlib", &SYSROOT_SRC.to_path(dirs));
-}
-
-fn prepare_coretests(dirs: &Dirs) {
- let sysroot_src_orig = get_default_sysroot(Path::new("rustc")).join("lib/rustlib/src/rust");
- assert!(sysroot_src_orig.exists());
-
- eprintln!("[COPY] coretests src");
-
- fs::create_dir_all(LIBCORE_TESTS_SRC.to_path(dirs)).unwrap();
- copy_dir_recursively(
- &sysroot_src_orig.join("library/core/tests"),
- &LIBCORE_TESTS_SRC.to_path(dirs),
- );
-
- eprintln!("[GIT] init");
- init_git_repo(&LIBCORE_TESTS_SRC.to_path(dirs));
-
- apply_patches(dirs, "coretests", &LIBCORE_TESTS_SRC.to_path(dirs));
+ apply_patches(dirs, "stdlib", &sysroot_src_orig, &STDLIB_SRC.to_path(dirs));
+
+ std::fs::write(
+ STDLIB_SRC.to_path(dirs).join("Cargo.toml"),
+ r#"
+[workspace]
+members = ["./library/sysroot"]
+
+[patch.crates-io]
+rustc-std-workspace-core = { path = "./library/rustc-std-workspace-core" }
+rustc-std-workspace-alloc = { path = "./library/rustc-std-workspace-alloc" }
+rustc-std-workspace-std = { path = "./library/rustc-std-workspace-std" }
+
+# Mandatory for correctly compiling compiler-builtins
+[profile.dev.package.compiler_builtins]
+debug-assertions = false
+overflow-checks = false
+codegen-units = 10000
+
+[profile.release.package.compiler_builtins]
+debug-assertions = false
+overflow-checks = false
+codegen-units = 10000
+"#,
+ )
+ .unwrap();
+
+ let source_lockfile = RelPath::PATCHES.to_path(dirs).join("stdlib-lock.toml");
+ let target_lockfile = STDLIB_SRC.to_path(dirs).join("Cargo.lock");
+ fs::copy(source_lockfile, target_lockfile).unwrap();
}
pub(crate) struct GitRepo {
url: GitRepoUrl,
rev: &'static str,
+ content_hash: &'static str,
patch_name: &'static str,
}
@@ -81,35 +64,107 @@ enum GitRepoUrl {
Github { user: &'static str, repo: &'static str },
}
+// Note: This uses a hasher which is not cryptographically secure. This is fine as the hash is meant
+// to protect against accidental modification and outdated downloads, not against manipulation.
+fn hash_file(file: &std::path::Path) -> u64 {
+ let contents = std::fs::read(file).unwrap();
+ #[allow(deprecated)]
+ let mut hasher = std::hash::SipHasher::new();
+ std::hash::Hash::hash(&contents, &mut hasher);
+ std::hash::Hasher::finish(&hasher)
+}
+
+fn hash_dir(dir: &std::path::Path) -> u64 {
+ let mut sub_hashes = std::collections::BTreeMap::new();
+ for entry in std::fs::read_dir(dir).unwrap() {
+ let entry = entry.unwrap();
+ if entry.file_type().unwrap().is_dir() {
+ sub_hashes
+ .insert(entry.file_name().to_str().unwrap().to_owned(), hash_dir(&entry.path()));
+ } else {
+ sub_hashes
+ .insert(entry.file_name().to_str().unwrap().to_owned(), hash_file(&entry.path()));
+ }
+ }
+ #[allow(deprecated)]
+ let mut hasher = std::hash::SipHasher::new();
+ std::hash::Hash::hash(&sub_hashes, &mut hasher);
+ std::hash::Hasher::finish(&hasher)
+}
+
impl GitRepo {
pub(crate) const fn github(
user: &'static str,
repo: &'static str,
rev: &'static str,
+ content_hash: &'static str,
patch_name: &'static str,
) -> GitRepo {
- GitRepo { url: GitRepoUrl::Github { user, repo }, rev, patch_name }
+ GitRepo { url: GitRepoUrl::Github { user, repo }, rev, content_hash, patch_name }
+ }
+
+ fn download_dir(&self, dirs: &Dirs) -> PathBuf {
+ match self.url {
+ GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo).to_path(dirs),
+ }
}
pub(crate) const fn source_dir(&self) -> RelPath {
match self.url {
- GitRepoUrl::Github { user: _, repo } => RelPath::DOWNLOAD.join(repo),
+ GitRepoUrl::Github { user: _, repo } => RelPath::BUILD.join(repo),
}
}
pub(crate) fn fetch(&self, dirs: &Dirs) {
+ let download_dir = self.download_dir(dirs);
+
+ if download_dir.exists() {
+ let actual_hash = format!("{:016x}", hash_dir(&download_dir));
+ if actual_hash == self.content_hash {
+ println!("[FRESH] {}", download_dir.display());
+ return;
+ } else {
+ println!(
+ "Mismatched content hash for {download_dir}: {actual_hash} != {content_hash}. Downloading again.",
+ download_dir = download_dir.display(),
+ content_hash = self.content_hash,
+ );
+ }
+ }
+
match self.url {
GitRepoUrl::Github { user, repo } => {
- clone_repo_shallow_github(
- dirs,
- &self.source_dir().to_path(dirs),
- user,
- repo,
- self.rev,
- );
+ clone_repo_shallow_github(dirs, &download_dir, user, repo, self.rev);
}
}
- apply_patches(dirs, self.patch_name, &self.source_dir().to_path(dirs));
+
+ let source_lockfile =
+ RelPath::PATCHES.to_path(dirs).join(format!("{}-lock.toml", self.patch_name));
+ let target_lockfile = download_dir.join("Cargo.lock");
+ if source_lockfile.exists() {
+ fs::copy(source_lockfile, target_lockfile).unwrap();
+ } else {
+ assert!(target_lockfile.exists());
+ }
+
+ let actual_hash = format!("{:016x}", hash_dir(&download_dir));
+ if actual_hash != self.content_hash {
+ println!(
+ "Download of {download_dir} failed with mismatched content hash: {actual_hash} != {content_hash}",
+ download_dir = download_dir.display(),
+ content_hash = self.content_hash,
+ );
+ std::process::exit(1);
+ }
+ }
+
+ pub(crate) fn patch(&self, dirs: &Dirs) {
+ apply_patches(
+ dirs,
+ self.patch_name,
+ &self.download_dir(dirs),
+ &self.source_dir().to_path(dirs),
+ );
}
}
@@ -126,6 +181,8 @@ fn clone_repo(download_dir: &Path, repo: &str, rev: &str) {
let mut checkout_cmd = git_command(download_dir, "checkout");
checkout_cmd.arg("-q").arg(rev);
spawn_and_wait(checkout_cmd);
+
+ std::fs::remove_dir_all(download_dir.join(".git")).unwrap();
}
fn clone_repo_shallow_github(dirs: &Dirs, download_dir: &Path, user: &str, repo: &str, rev: &str) {
@@ -173,8 +230,6 @@ fn clone_repo_shallow_github(dirs: &Dirs, download_dir: &Path, user: &str, repo:
// Rename unpacked dir to the expected name
std::fs::rename(archive_dir, &download_dir).unwrap();
- init_git_repo(&download_dir);
-
// Cleanup
std::fs::remove_file(archive_file).unwrap();
}
@@ -213,7 +268,22 @@ fn get_patches(dirs: &Dirs, crate_name: &str) -> Vec<PathBuf> {
patches
}
-fn apply_patches(dirs: &Dirs, crate_name: &str, target_dir: &Path) {
+pub(crate) fn apply_patches(dirs: &Dirs, crate_name: &str, source_dir: &Path, target_dir: &Path) {
+ // FIXME avoid copy and patch if src, patches and target are unchanged
+
+ eprintln!("[COPY] {crate_name} source");
+
+ remove_dir_if_exists(target_dir);
+ fs::create_dir_all(target_dir).unwrap();
+ if crate_name == "stdlib" {
+ fs::create_dir(target_dir.join("library")).unwrap();
+ copy_dir_recursively(&source_dir.join("library"), &target_dir.join("library"));
+ } else {
+ copy_dir_recursively(source_dir, target_dir);
+ }
+
+ init_git_repo(target_dir);
+
if crate_name == "<none>" {
return;
}
diff --git a/compiler/rustc_codegen_cranelift/build_system/rustc_info.rs b/compiler/rustc_codegen_cranelift/build_system/rustc_info.rs
index a70453b44..5b71504e9 100644
--- a/compiler/rustc_codegen_cranelift/build_system/rustc_info.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/rustc_info.rs
@@ -1,15 +1,9 @@
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
-pub(crate) fn get_rustc_version(rustc: &Path) -> String {
+pub(crate) fn get_host_triple(rustc: &Path) -> String {
let version_info =
- Command::new(rustc).stderr(Stdio::inherit()).args(&["-V"]).output().unwrap().stdout;
- String::from_utf8(version_info).unwrap()
-}
-
-pub(crate) fn get_host_triple() -> String {
- let version_info =
- Command::new("rustc").stderr(Stdio::inherit()).args(&["-vV"]).output().unwrap().stdout;
+ Command::new(rustc).stderr(Stdio::inherit()).args(&["-vV"]).output().unwrap().stdout;
String::from_utf8(version_info)
.unwrap()
.lines()
@@ -34,6 +28,9 @@ pub(crate) fn get_toolchain_name() -> String {
}
pub(crate) fn get_cargo_path() -> PathBuf {
+ if let Ok(cargo) = std::env::var("CARGO") {
+ return PathBuf::from(cargo);
+ }
let cargo_path = Command::new("rustup")
.stderr(Stdio::inherit())
.args(&["which", "cargo"])
@@ -44,6 +41,9 @@ pub(crate) fn get_cargo_path() -> PathBuf {
}
pub(crate) fn get_rustc_path() -> PathBuf {
+ if let Ok(rustc) = std::env::var("RUSTC") {
+ return PathBuf::from(rustc);
+ }
let rustc_path = Command::new("rustup")
.stderr(Stdio::inherit())
.args(&["which", "rustc"])
@@ -54,6 +54,9 @@ pub(crate) fn get_rustc_path() -> PathBuf {
}
pub(crate) fn get_rustdoc_path() -> PathBuf {
+ if let Ok(rustdoc) = std::env::var("RUSTDOC") {
+ return PathBuf::from(rustdoc);
+ }
let rustc_path = Command::new("rustup")
.stderr(Stdio::inherit())
.args(&["which", "rustdoc"])
@@ -73,8 +76,9 @@ pub(crate) fn get_default_sysroot(rustc: &Path) -> PathBuf {
Path::new(String::from_utf8(default_sysroot).unwrap().trim()).to_owned()
}
-pub(crate) fn get_file_name(crate_name: &str, crate_type: &str) -> String {
- let file_name = Command::new("rustc")
+// FIXME call once for each target and pass result around in struct
+pub(crate) fn get_file_name(rustc: &Path, crate_name: &str, crate_type: &str) -> String {
+ let file_name = Command::new(rustc)
.stderr(Stdio::inherit())
.args(&[
"--crate-name",
diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs
index 0c25b4aad..08d8f708c 100644
--- a/compiler/rustc_codegen_cranelift/build_system/tests.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs
@@ -1,13 +1,14 @@
use super::build_sysroot;
use super::config;
use super::path::{Dirs, RelPath};
-use super::prepare::GitRepo;
+use super::prepare::{apply_patches, GitRepo};
+use super::rustc_info::get_default_sysroot;
use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler};
-use super::SysrootKind;
+use super::{CodegenBackend, SysrootKind};
use std::env;
use std::ffi::OsStr;
use std::fs;
-use std::path::Path;
+use std::path::PathBuf;
use std::process::Command;
static BUILD_EXAMPLE_OUT_DIR: RelPath = RelPath::BUILD.join("example");
@@ -18,7 +19,7 @@ struct TestCase {
}
enum TestCaseCmd {
- Custom { func: &'static dyn Fn(&TestRunner) },
+ Custom { func: &'static dyn Fn(&TestRunner<'_>) },
BuildLib { source: &'static str, crate_types: &'static str },
BuildBinAndRun { source: &'static str, args: &'static [&'static str] },
JitBin { source: &'static str, args: &'static str },
@@ -26,7 +27,7 @@ enum TestCaseCmd {
impl TestCase {
// FIXME reduce usage of custom test case commands
- const fn custom(config: &'static str, func: &'static dyn Fn(&TestRunner)) -> Self {
+ const fn custom(config: &'static str, func: &'static dyn Fn(&TestRunner<'_>)) -> Self {
Self { config, cmd: TestCaseCmd::Custom { func } }
}
@@ -95,32 +96,45 @@ const BASE_SYSROOT_SUITE: &[TestCase] = &[
// FIXME(rust-random/rand#1293): Newer rand versions fail to test on Windows. Update once this is
// fixed.
-pub(crate) static RAND_REPO: GitRepo =
- GitRepo::github("rust-random", "rand", "50b9a447410860af8d6db9a208c3576886955874", "rand");
+pub(crate) static RAND_REPO: GitRepo = GitRepo::github(
+ "rust-random",
+ "rand",
+ "50b9a447410860af8d6db9a208c3576886955874",
+ "446203b96054891e",
+ "rand",
+);
-pub(crate) static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir(), "rand");
+pub(crate) static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir(), "rand_target");
-pub(crate) static REGEX_REPO: GitRepo =
- GitRepo::github("rust-lang", "regex", "32fed9429eafba0ae92a64b01796a0c5a75b88c8", "regex");
+pub(crate) static REGEX_REPO: GitRepo = GitRepo::github(
+ "rust-lang",
+ "regex",
+ "32fed9429eafba0ae92a64b01796a0c5a75b88c8",
+ "fcc4df7c5b902633",
+ "regex",
+);
-pub(crate) static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir(), "regex");
+pub(crate) static REGEX: CargoProject = CargoProject::new(&REGEX_REPO.source_dir(), "regex_target");
pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github(
"rust-lang",
"portable-simd",
"ad8afa8c81273b3b49acbea38cd3bcf17a34cf2b",
+ "800548f8000e31bd",
"portable-simd",
);
pub(crate) static PORTABLE_SIMD: CargoProject =
- CargoProject::new(&PORTABLE_SIMD_REPO.source_dir(), "portable_simd");
+ CargoProject::new(&PORTABLE_SIMD_REPO.source_dir(), "portable-simd_target");
-pub(crate) static LIBCORE_TESTS_SRC: RelPath = RelPath::DOWNLOAD.join("coretests_src");
+static LIBCORE_TESTS_SRC: RelPath = RelPath::BUILD.join("coretests");
-pub(crate) static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "core_tests");
+static LIBCORE_TESTS: CargoProject = CargoProject::new(&LIBCORE_TESTS_SRC, "coretests_target");
const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
TestCase::custom("test.rust-random/rand", &|runner| {
+ RAND_REPO.patch(&runner.dirs);
+
RAND.clean(&runner.dirs);
if runner.is_native {
@@ -135,6 +149,17 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
}
}),
TestCase::custom("test.libcore", &|runner| {
+ apply_patches(
+ &runner.dirs,
+ "coretests",
+ &runner.stdlib_source.join("library/core/tests"),
+ &LIBCORE_TESTS_SRC.to_path(&runner.dirs),
+ );
+
+ let source_lockfile = RelPath::PATCHES.to_path(&runner.dirs).join("coretests-lock.toml");
+ let target_lockfile = LIBCORE_TESTS_SRC.to_path(&runner.dirs).join("Cargo.lock");
+ fs::copy(source_lockfile, target_lockfile).unwrap();
+
LIBCORE_TESTS.clean(&runner.dirs);
if runner.is_native {
@@ -149,6 +174,8 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
}
}),
TestCase::custom("test.regex-shootout-regex-dna", &|runner| {
+ REGEX_REPO.patch(&runner.dirs);
+
REGEX.clean(&runner.dirs);
let mut build_cmd = REGEX.build(&runner.target_compiler, &runner.dirs);
@@ -181,6 +208,8 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
}
}),
TestCase::custom("test.regex", &|runner| {
+ REGEX_REPO.patch(&runner.dirs);
+
REGEX.clean(&runner.dirs);
if runner.is_native {
@@ -197,6 +226,8 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
}
}),
TestCase::custom("test.portable-simd", &|runner| {
+ PORTABLE_SIMD_REPO.patch(&runner.dirs);
+
PORTABLE_SIMD.clean(&runner.dirs);
let mut build_cmd = PORTABLE_SIMD.build(&runner.target_compiler, &runner.dirs);
@@ -215,24 +246,35 @@ pub(crate) fn run_tests(
dirs: &Dirs,
channel: &str,
sysroot_kind: SysrootKind,
- cg_clif_dylib: &Path,
+ use_unstable_features: bool,
+ skip_tests: &[&str],
+ cg_clif_dylib: &CodegenBackend,
bootstrap_host_compiler: &Compiler,
+ rustup_toolchain_name: Option<&str>,
target_triple: String,
) {
- if config::get_bool("testsuite.no_sysroot") {
+ let stdlib_source =
+ get_default_sysroot(&bootstrap_host_compiler.rustc).join("lib/rustlib/src/rust");
+ assert!(stdlib_source.exists());
+
+ if config::get_bool("testsuite.no_sysroot") && !skip_tests.contains(&"testsuite.no_sysroot") {
let target_compiler = build_sysroot::build_sysroot(
dirs,
channel,
SysrootKind::None,
cg_clif_dylib,
bootstrap_host_compiler,
+ rustup_toolchain_name,
target_triple.clone(),
);
let runner = TestRunner::new(
dirs.clone(),
target_compiler,
+ use_unstable_features,
+ skip_tests,
bootstrap_host_compiler.triple == target_triple,
+ stdlib_source.clone(),
);
BUILD_EXAMPLE_OUT_DIR.ensure_fresh(dirs);
@@ -241,23 +283,32 @@ pub(crate) fn run_tests(
eprintln!("[SKIP] no_sysroot tests");
}
- let run_base_sysroot = config::get_bool("testsuite.base_sysroot");
- let run_extended_sysroot = config::get_bool("testsuite.extended_sysroot");
+ let run_base_sysroot = config::get_bool("testsuite.base_sysroot")
+ && !skip_tests.contains(&"testsuite.base_sysroot");
+ let run_extended_sysroot = config::get_bool("testsuite.extended_sysroot")
+ && !skip_tests.contains(&"testsuite.extended_sysroot");
if run_base_sysroot || run_extended_sysroot {
- let target_compiler = build_sysroot::build_sysroot(
+ let mut target_compiler = build_sysroot::build_sysroot(
dirs,
channel,
sysroot_kind,
cg_clif_dylib,
bootstrap_host_compiler,
+ rustup_toolchain_name,
target_triple.clone(),
);
+ // Rust's build system denies a couple of lints that trigger on several of the test
+ // projects. Changing the code to fix them is not worth it, so just silence all lints.
+ target_compiler.rustflags += " --cap-lints=allow";
let runner = TestRunner::new(
dirs.clone(),
target_compiler,
+ use_unstable_features,
+ skip_tests,
bootstrap_host_compiler.triple == target_triple,
+ stdlib_source,
);
if run_base_sysroot {
@@ -274,15 +325,25 @@ pub(crate) fn run_tests(
}
}
-struct TestRunner {
+struct TestRunner<'a> {
is_native: bool,
jit_supported: bool,
+ use_unstable_features: bool,
+ skip_tests: &'a [&'a str],
dirs: Dirs,
target_compiler: Compiler,
+ stdlib_source: PathBuf,
}
-impl TestRunner {
- fn new(dirs: Dirs, mut target_compiler: Compiler, is_native: bool) -> Self {
+impl<'a> TestRunner<'a> {
+ fn new(
+ dirs: Dirs,
+ mut target_compiler: Compiler,
+ use_unstable_features: bool,
+ skip_tests: &'a [&'a str],
+ is_native: bool,
+ stdlib_source: PathBuf,
+ ) -> Self {
if let Ok(rustflags) = env::var("RUSTFLAGS") {
target_compiler.rustflags.push(' ');
target_compiler.rustflags.push_str(&rustflags);
@@ -297,11 +358,20 @@ impl TestRunner {
target_compiler.rustflags.push_str(" -Clink-arg=-undefined -Clink-arg=dynamic_lookup");
}
- let jit_supported = is_native
+ let jit_supported = use_unstable_features
+ && is_native
&& target_compiler.triple.contains("x86_64")
&& !target_compiler.triple.contains("windows");
- Self { is_native, jit_supported, dirs, target_compiler }
+ Self {
+ is_native,
+ jit_supported,
+ use_unstable_features,
+ skip_tests,
+ dirs,
+ target_compiler,
+ stdlib_source,
+ }
}
fn run_testsuite(&self, tests: &[TestCase]) {
@@ -310,7 +380,10 @@ impl TestRunner {
let tag = tag.to_uppercase();
let is_jit_test = tag == "JIT";
- if !config::get_bool(config) || (is_jit_test && !self.jit_supported) {
+ if !config::get_bool(config)
+ || (is_jit_test && !self.jit_supported)
+ || self.skip_tests.contains(&config)
+ {
eprintln!("[{tag}] {testname} (skipped)");
continue;
} else {
@@ -320,10 +393,24 @@ impl TestRunner {
match *cmd {
TestCaseCmd::Custom { func } => func(self),
TestCaseCmd::BuildLib { source, crate_types } => {
- self.run_rustc([source, "--crate-type", crate_types]);
+ if self.use_unstable_features {
+ self.run_rustc([source, "--crate-type", crate_types]);
+ } else {
+ self.run_rustc([
+ source,
+ "--crate-type",
+ crate_types,
+ "--cfg",
+ "no_unstable_features",
+ ]);
+ }
}
TestCaseCmd::BuildBinAndRun { source, args } => {
- self.run_rustc([source]);
+ if self.use_unstable_features {
+ self.run_rustc([source]);
+ } else {
+ self.run_rustc([source, "--cfg", "no_unstable_features"]);
+ }
self.run_out_command(
source.split('/').last().unwrap().split('.').next().unwrap(),
args,
diff --git a/compiler/rustc_codegen_cranelift/build_system/usage.txt b/compiler/rustc_codegen_cranelift/build_system/usage.txt
index ab98ccc35..6d3b3a13d 100644
--- a/compiler/rustc_codegen_cranelift/build_system/usage.txt
+++ b/compiler/rustc_codegen_cranelift/build_system/usage.txt
@@ -1,11 +1,11 @@
The build system of cg_clif.
USAGE:
- ./y.rs prepare [--out-dir DIR]
- ./y.rs build [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
- ./y.rs test [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
- ./y.rs abi-cafe [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
- ./y.rs bench [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
+ ./y.sh prepare [--out-dir DIR] [--download-dir DIR]
+ ./y.sh build [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--download-dir DIR] [--no-unstable-features] [--frozen]
+ ./y.sh test [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--download-dir DIR] [--no-unstable-features] [--frozen] [--skip-test TESTNAME]
+ ./y.sh abi-cafe [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--download-dir DIR] [--no-unstable-features] [--frozen]
+ ./y.sh bench [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--download-dir DIR] [--no-unstable-features] [--frozen]
OPTIONS:
--debug
@@ -22,14 +22,28 @@ OPTIONS:
Specify the directory in which the download, build and dist directories are stored.
By default this is the working directory.
+ --download-dir DIR
+ Specify the directory in which the download directory is stored. Overrides --out-dir.
+
--no-unstable-features
Some features are not yet ready for production usage. This option will disable these
features. This includes the JIT mode and inline assembly support.
+ --frozen
+ Require Cargo.lock and cache are up to date
+
+ --skip-test TESTNAME
+ Skip testing the TESTNAME test. The test name format is the same as config.txt.
+
+ --use-backend NAME
+ Use the existing Cranelift (or other) backend of the rustc with which we built.
+ Warning: This is meant for use in rust's CI only!
+
REQUIREMENTS:
- * Rustup: The build system has a hard coded dependency on rustup to install the right nightly
- version and make sure it is used where necessary.
- * Git: `./y.rs prepare` uses git for applying patches and on Windows for downloading test repos.
- * Curl and tar (non-Windows only): Used by `./y.rs prepare` to download a single commit for
+ * Rustup: By default rustup is used to install the right nightly version. If you don't want to
+ use rustup, you can manually install the nightly version indicated by rust-toolchain.toml and
+ point the CARGO, RUSTC and RUSTDOC env vars to the right executables.
+ * Git: `./y.sh prepare` uses git for applying patches and on Windows for downloading test repos.
+ * Curl and tar (non-Windows only): Used by `./y.sh prepare` to download a single commit for
repos. Git will be used to clone the whole repo when using Windows.
- * [Hyperfine](https://github.com/sharkdp/hyperfine/): Used for benchmarking with `./y.rs bench`.
+ * [Hyperfine](https://github.com/sharkdp/hyperfine/): Used for benchmarking with `./y.sh bench`.
diff --git a/compiler/rustc_codegen_cranelift/build_system/utils.rs b/compiler/rustc_codegen_cranelift/build_system/utils.rs
index abc5bab49..41fc366e2 100644
--- a/compiler/rustc_codegen_cranelift/build_system/utils.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/utils.rs
@@ -5,7 +5,6 @@ use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_cargo_path, get_rustc_path, get_rustdoc_path};
#[derive(Clone, Debug)]
pub(crate) struct Compiler {
@@ -19,18 +18,6 @@ pub(crate) struct Compiler {
}
impl Compiler {
- pub(crate) fn bootstrap_with_triple(triple: String) -> Compiler {
- Compiler {
- cargo: get_cargo_path(),
- rustc: get_rustc_path(),
- rustdoc: get_rustdoc_path(),
- rustflags: String::new(),
- rustdocflags: String::new(),
- triple,
- runner: vec![],
- }
- }
-
pub(crate) fn set_cross_linker_and_runner(&mut self) {
match self.triple.as_str() {
"aarch64-unknown-linux-gnu" => {
@@ -95,7 +82,11 @@ impl CargoProject {
.arg(self.manifest_path(dirs))
.arg("--target-dir")
.arg(self.target_dir(dirs))
- .arg("--frozen");
+ .arg("--locked");
+
+ if dirs.frozen {
+ cmd.arg("--frozen");
+ }
cmd
}
@@ -120,23 +111,6 @@ impl CargoProject {
cmd
}
- #[must_use]
- pub(crate) fn fetch(
- &self,
- cargo: impl AsRef<Path>,
- rustc: impl AsRef<Path>,
- dirs: &Dirs,
- ) -> Command {
- let mut cmd = Command::new(cargo.as_ref());
-
- cmd.env("RUSTC", rustc.as_ref())
- .arg("fetch")
- .arg("--manifest-path")
- .arg(self.manifest_path(dirs));
-
- cmd
- }
-
pub(crate) fn clean(&self, dirs: &Dirs) {
let _ = fs::remove_dir_all(self.target_dir(dirs));
}
@@ -162,8 +136,7 @@ pub(crate) fn hyperfine_command(
warmup: u64,
runs: u64,
prepare: Option<&str>,
- a: &str,
- b: &str,
+ cmds: &[&str],
) -> Command {
let mut bench = Command::new("hyperfine");
@@ -179,7 +152,7 @@ pub(crate) fn hyperfine_command(
bench.arg("--prepare").arg(prepare);
}
- bench.arg(a).arg(b);
+ bench.args(cmds);
bench
}
@@ -285,3 +258,13 @@ pub(crate) fn is_ci() -> bool {
pub(crate) fn is_ci_opt() -> bool {
env::var("CI_OPT").is_ok()
}
+
+pub(crate) fn maybe_incremental(cmd: &mut Command) {
+ if is_ci() || std::env::var("CARGO_BUILD_INCREMENTAL").map_or(false, |val| val == "false") {
+ // Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
+ cmd.env("CARGO_BUILD_INCREMENTAL", "false");
+ } else {
+ // Force incr comp even in release mode unless in CI or incremental builds are explicitly disabled
+ cmd.env("CARGO_BUILD_INCREMENTAL", "true");
+ }
+}
diff --git a/compiler/rustc_codegen_cranelift/clean_all.sh b/compiler/rustc_codegen_cranelift/clean_all.sh
index cdfc2e143..19405a53d 100755
--- a/compiler/rustc_codegen_cranelift/clean_all.sh
+++ b/compiler/rustc_codegen_cranelift/clean_all.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -e
-rm -rf target/ download/ build/ dist/ y.bin y.bin.dSYM y.exe y.pdb
+rm -rf target/ build_system/target download/ build/ dist/ y.bin y.bin.dSYM y.exe y.pdb
# Kept for now in case someone updates their checkout of cg_clif before running clean_all.sh
# FIXME remove at some point in the future
diff --git a/compiler/rustc_codegen_cranelift/docs/usage.md b/compiler/rustc_codegen_cranelift/docs/usage.md
index 4c2b0fa17..c6210f958 100644
--- a/compiler/rustc_codegen_cranelift/docs/usage.md
+++ b/compiler/rustc_codegen_cranelift/docs/usage.md
@@ -2,7 +2,7 @@
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 (`y.rs prepare` and `y.rs build` or `test.sh`).
+Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`y.sh prepare` and `y.sh build` or `test.sh`).
## Cargo
diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs
index d994e2fbc..117eed5af 100644
--- a/compiler/rustc_codegen_cranelift/example/alloc_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs
@@ -1,4 +1,4 @@
-#![feature(start, core_intrinsics, alloc_error_handler)]
+#![feature(start, core_intrinsics, alloc_error_handler, lang_items)]
#![no_std]
extern crate alloc;
@@ -27,6 +27,11 @@ fn alloc_error_handler(_: alloc::alloc::Layout) -> ! {
core::intrinsics::abort();
}
+#[lang = "eh_personality"]
+fn eh_personality() -> ! {
+ loop {}
+}
+
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let world: Box<&str> = Box::new("Hello World!\0");
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs
index ea97e9f06..79ca4c039 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs
@@ -502,6 +502,9 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
drop_in_place(to_drop);
}
+#[lang = "unpin"]
+pub auto trait Unpin {}
+
#[lang = "deref"]
pub trait Deref {
type Target: ?Sized;
@@ -526,7 +529,7 @@ impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsiz
impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
#[lang = "owned_box"]
-pub struct Box<T: ?Sized>(Unique<T>, ());
+pub struct Box<T: ?Sized, A = ()>(Unique<T>, A);
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
@@ -541,9 +544,10 @@ impl<T> Box<T> {
}
}
-impl<T: ?Sized> Drop for Box<T> {
+impl<T: ?Sized, A> Drop for Box<T, A> {
fn drop(&mut self) {
- // drop is currently performed by compiler.
+ // inner value is dropped by compiler
+ libc::free(self.0.pointer.0 as *mut u8);
}
}
@@ -560,11 +564,6 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
libc::malloc(size)
}
-#[lang = "box_free"]
-unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, _alloc: ()) {
- libc::free(ptr.pointer.0 as *mut u8);
-}
-
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);
diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
index 5a55aa215..d97fab9eb 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -322,7 +322,12 @@ fn main() {
#[cfg(all(not(jit), not(all(windows, target_env = "gnu"))))]
test_tls();
- #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "darwin")))]
+ #[cfg(all(
+ not(jit),
+ not(no_unstable_features),
+ target_arch = "x86_64",
+ any(target_os = "linux", target_os = "macos")
+ ))]
unsafe {
global_asm_test();
}
@@ -350,12 +355,17 @@ fn main() {
let _a = f.0[0];
}
-#[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "darwin")))]
+#[cfg(all(
+ not(jit),
+ not(no_unstable_features),
+ target_arch = "x86_64",
+ any(target_os = "linux", target_os = "macos")
+))]
extern "C" {
fn global_asm_test();
}
-#[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))]
+#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "linux"))]
global_asm! {
"
.global global_asm_test
@@ -365,7 +375,7 @@ global_asm! {
"
}
-#[cfg(all(not(jit), target_arch = "x86_64", target_os = "darwin"))]
+#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "macos"))]
global_asm! {
"
.global _global_asm_test
diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs
index ab4045d11..1bf0ff64c 100644
--- a/compiler/rustc_codegen_cranelift/example/std_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/std_example.rs
@@ -197,6 +197,10 @@ unsafe fn test_simd() {
test_mm_extract_epi8();
test_mm_insert_epi16();
+ test_mm_shuffle_epi8();
+
+ test_mm256_shuffle_epi8();
+ test_mm256_permute2x128_si256();
#[rustfmt::skip]
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)));
@@ -294,6 +298,12 @@ pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
}
#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "avx")]
+pub unsafe fn assert_eq_m256i(a: __m256i, b: __m256i) {
+ assert_eq!(std::mem::transmute::<_, [u64; 4]>(a), std::mem::transmute::<_, [u64; 4]>(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]));
@@ -336,6 +346,64 @@ unsafe fn test_mm_insert_epi16() {
assert_eq_m128i(r, e);
}
+#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "ssse3")]
+unsafe fn test_mm_shuffle_epi8() {
+ #[rustfmt::skip]
+ let a = _mm_setr_epi8(
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 16,
+ );
+ #[rustfmt::skip]
+ let b = _mm_setr_epi8(
+ 4, 128_u8 as i8, 4, 3,
+ 24, 12, 6, 19,
+ 12, 5, 5, 10,
+ 4, 1, 8, 0,
+ );
+ let expected = _mm_setr_epi8(5, 0, 5, 4, 9, 13, 7, 4, 13, 6, 6, 11, 5, 2, 9, 1);
+ let r = _mm_shuffle_epi8(a, b);
+ assert_eq_m128i(r, expected);
+}
+
+#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "avx2")]
+unsafe fn test_mm256_shuffle_epi8() {
+ #[rustfmt::skip]
+ let a = _mm256_setr_epi8(
+ 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,
+ );
+ #[rustfmt::skip]
+ let b = _mm256_setr_epi8(
+ 4, 128u8 as i8, 4, 3, 24, 12, 6, 19,
+ 12, 5, 5, 10, 4, 1, 8, 0,
+ 4, 128u8 as i8, 4, 3, 24, 12, 6, 19,
+ 12, 5, 5, 10, 4, 1, 8, 0,
+ );
+ #[rustfmt::skip]
+ let expected = _mm256_setr_epi8(
+ 5, 0, 5, 4, 9, 13, 7, 4,
+ 13, 6, 6, 11, 5, 2, 9, 1,
+ 21, 0, 21, 20, 25, 29, 23, 20,
+ 29, 22, 22, 27, 21, 18, 25, 17,
+ );
+ let r = _mm256_shuffle_epi8(a, b);
+ assert_eq_m256i(r, expected);
+}
+
+#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "avx2")]
+unsafe fn test_mm256_permute2x128_si256() {
+ let a = _mm256_setr_epi64x(100, 200, 500, 600);
+ let b = _mm256_setr_epi64x(300, 400, 700, 800);
+ let r = _mm256_permute2x128_si256::<0b00_01_00_11>(a, b);
+ let e = _mm256_setr_epi64x(700, 800, 500, 600);
+ assert_eq_m256i(r, e);
+}
+
fn test_checked_mul() {
let u: Option<u8> = u8::from_str_radix("1000", 10).ok();
assert_eq!(u, None);
diff --git a/compiler/rustc_codegen_cranelift/patches/0023-coretests-Ignore-failing-tests.patch b/compiler/rustc_codegen_cranelift/patches/0023-coretests-Ignore-failing-tests.patch
index f2cb82751..385f5a8a2 100644
--- a/compiler/rustc_codegen_cranelift/patches/0023-coretests-Ignore-failing-tests.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0023-coretests-Ignore-failing-tests.patch
@@ -10,42 +10,6 @@ Subject: [PATCH] [core] Ignore failing tests
library/core/tests/time.rs | 1 +
4 files changed, 18 insertions(+), 2 deletions(-)
-diff --git a/array.rs b/array.rs
-index 4bc44e9..8e3c7a4 100644
---- a/array.rs
-+++ b/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/atomic.rs b/atomic.rs
index 13b12db..96fe4b9 100644
--- a/atomic.rs
diff --git a/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml b/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml
new file mode 100644
index 000000000..af8f28a19
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/coretests-lock.toml
@@ -0,0 +1,35 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "coretests"
+version = "0.0.0"
+dependencies = [
+ "rand",
+ "rand_xorshift",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
diff --git a/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml b/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml
new file mode 100644
index 000000000..e7db1fd2c
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/portable-simd-lock.toml
@@ -0,0 +1,304 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bumpalo"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "console_error_panic_hook"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "core_simd"
+version = "0.1.0"
+dependencies = [
+ "proptest",
+ "std_float",
+ "test_helpers",
+ "wasm-bindgen",
+ "wasm-bindgen-test",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "log"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.17.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "proptest"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12e6c80c1139113c28ee4670dc50cc42915228b51f56a9e407f0ec60f966646f"
+dependencies = [
+ "bitflags",
+ "byteorder",
+ "num-traits",
+ "rand",
+ "rand_chacha",
+ "rand_xorshift",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
+dependencies = [
+ "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"
+
+[[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 = "rand_xorshift"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "std_float"
+version = "0.1.0"
+dependencies = [
+ "core_simd",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "test_helpers"
+version = "0.1.0"
+dependencies = [
+ "proptest",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
+
+[[package]]
+name = "wasm-bindgen-test"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e636f3a428ff62b3742ebc3c70e254dfe12b8c2b469d688ea59cdd4abcf502"
+dependencies = [
+ "console_error_panic_hook",
+ "js-sys",
+ "scoped-tls",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-bindgen-test-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-test-macro"
+version = "0.3.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18c1fad2f7c4958e7bcce014fa212f59a65d5e3721d0f77e6c0b27ede936ba3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.63"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
diff --git a/compiler/rustc_codegen_cranelift/patches/rand-lock.toml b/compiler/rustc_codegen_cranelift/patches/rand-lock.toml
new file mode 100644
index 000000000..66c515731
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/rand-lock.toml
@@ -0,0 +1,346 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "average"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843ec791d3f24503bbf72bbd5e49a3ab4dbb4bcd0a8ef6b0c908efa73caa27b1"
+dependencies = [
+ "easy-cast",
+ "float-ord",
+ "num-traits",
+]
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "easy-cast"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4bd102ee8c418348759919b83b81cdbdc933ffe29740b903df448b4bafaa348e"
+dependencies = [
+ "libm",
+]
+
+[[package]]
+name = "either"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+
+[[package]]
+name = "float-ord"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
+
+[[package]]
+name = "getrandom"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+
+[[package]]
+name = "libc"
+version = "0.2.144"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+
+[[package]]
+name = "libm"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
+
+[[package]]
+name = "log"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
+
+[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.9.0"
+dependencies = [
+ "bincode",
+ "libc",
+ "log",
+ "rand_chacha",
+ "rand_core",
+ "rand_pcg",
+ "rayon",
+ "serde",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.4.0"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.7.0"
+dependencies = [
+ "getrandom",
+ "serde",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.5.0"
+dependencies = [
+ "average",
+ "num-traits",
+ "rand",
+ "rand_pcg",
+ "serde",
+ "special",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.4.0"
+dependencies = [
+ "bincode",
+ "rand_core",
+ "serde",
+]
+
+[[package]]
+name = "rayon"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "num_cpus",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "serde"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "special"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24a65e074159b75dcf173a4733ab2188baac24967b5c8ec9ed87ae15fcbc7636"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
diff --git a/compiler/rustc_codegen_cranelift/patches/regex-lock.toml b/compiler/rustc_codegen_cranelift/patches/regex-lock.toml
new file mode 100644
index 000000000..0e4a33b90
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/patches/regex-lock.toml
@@ -0,0 +1,439 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bzip2"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
+dependencies = [
+ "bzip2-sys",
+ "libc",
+]
+
+[[package]]
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+
+[[package]]
+name = "cfg-if"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "docopt"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f3f119846c823f9eafcf953a8f6ffb6ed69bf6240883261a7f13b634579a51f"
+dependencies = [
+ "lazy_static",
+ "regex 1.8.3",
+ "serde",
+ "strsim",
+]
+
+[[package]]
+name = "filetime"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "redox_syscall",
+ "windows-sys",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi",
+]
+
+[[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.144"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+
+[[package]]
+name = "libpcre-sys"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ff3dd28ba96d6fe6752882f2f1b25ba8e1646448e79042442347cf3a92a6666"
+dependencies = [
+ "bzip2",
+ "libc",
+ "pkg-config",
+ "tar",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memmap"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2ffa2c986de11a9df78620c01eeaaf27d94d3ff02bf81bfcca953102dd0c6ff"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "onig"
+version = "3.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5eeb268a4620c74ea5768c6d2ccd492d60a47a8754666b91a46bfc35cd4d1ba"
+dependencies = [
+ "bitflags",
+ "lazy_static",
+ "libc",
+ "onig_sys",
+]
+
+[[package]]
+name = "onig_sys"
+version = "68.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "195ebddbb56740be48042ca117b8fb6e0d99fe392191a9362d82f5f69e510379"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.59"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quickcheck"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.2"
+dependencies = [
+ "aho-corasick",
+ "lazy_static",
+ "memchr",
+ "quickcheck",
+ "rand",
+ "regex-syntax 0.6.29",
+]
+
+[[package]]
+name = "regex"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
+dependencies = [
+ "regex-syntax 0.7.2",
+]
+
+[[package]]
+name = "regex-benchmark"
+version = "0.1.0"
+dependencies = [
+ "cc",
+ "cfg-if 0.1.10",
+ "docopt",
+ "lazy_static",
+ "libc",
+ "libpcre-sys",
+ "memmap",
+ "onig",
+ "pkg-config",
+ "regex 1.7.2",
+ "regex-syntax 0.6.29",
+ "serde",
+]
+
+[[package]]
+name = "regex-debug"
+version = "0.1.0"
+dependencies = [
+ "docopt",
+ "regex 1.7.2",
+ "regex-syntax 0.6.29",
+ "serde",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.29"
+
+[[package]]
+name = "regex-syntax"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+
+[[package]]
+name = "rure"
+version = "0.2.2"
+dependencies = [
+ "libc",
+ "regex 1.7.2",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.163"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
+[[package]]
+name = "syn"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tar"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+
+[[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"
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+
+[[package]]
+name = "xattr"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc"
+dependencies = [
+ "libc",
+]
diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
index 7ddf91ad0..1dde9e54d 100644
--- a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
@@ -30,9 +30,27 @@ version = "0.0.0"
dependencies = [
"compiler_builtins",
"core",
+ "rand",
+ "rand_xorshift",
]
[[package]]
+name = "auxv"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e50430f9beb8effb02399fa81c76eeaa26b05e4f03b09285cad8d079c1af5a3d"
+dependencies = [
+ "byteorder",
+ "gcc",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -50,16 +68,31 @@ dependencies = [
[[package]]
name = "compiler_builtins"
-version = "0.1.91"
+version = "0.1.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "571298a3cce7e2afbd3d61abb91a18667d5ab25993ec577a88ee8ac45f00cc3a"
+checksum = "76630810d973ecea3dbf611e1b7aecfb1012751ef1ff8de3998f89014a166781"
dependencies = [
+ "cc",
"rustc-std-workspace-core",
]
[[package]]
name = "core"
version = "0.0.0"
+dependencies = [
+ "rand",
+ "rand_xorshift",
+]
+
+[[package]]
+name = "cupid"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bad352a84b567cc38a5854e3aa8ee903cb8519a25d0b799b739bafffd1f91a1"
+dependencies = [
+ "gcc",
+ "rustc_version",
+]
[[package]]
name = "dlmalloc"
@@ -83,6 +116,12 @@ dependencies = [
]
[[package]]
+name = "gcc"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
+
+[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -106,9 +145,9 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.12.3"
+version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@@ -128,9 +167,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.142"
+version = "0.2.146"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
+checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
dependencies = [
"rustc-std-workspace-core",
]
@@ -159,9 +198,9 @@ dependencies = [
[[package]]
name = "object"
-version = "0.30.3"
+version = "0.30.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439"
+checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
dependencies = [
"compiler_builtins",
"memchr",
@@ -201,6 +240,39 @@ dependencies = [
]
[[package]]
+name = "profiler_builtins"
+version = "0.0.0"
+dependencies = [
+ "cc",
+ "compiler_builtins",
+ "core",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
+[[package]]
+name = "rand_xorshift"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -232,6 +304,30 @@ dependencies = [
]
[[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 = "std"
version = "0.0.0"
dependencies = [
@@ -249,6 +345,9 @@ dependencies = [
"object",
"panic_abort",
"panic_unwind",
+ "profiler_builtins",
+ "rand",
+ "rand_xorshift",
"rustc-demangle",
"std_detect",
"unwind",
@@ -259,8 +358,10 @@ dependencies = [
name = "std_detect"
version = "0.1.5"
dependencies = [
+ "auxv",
"cfg-if",
"compiler_builtins",
+ "cupid",
"libc",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
@@ -270,9 +371,7 @@ dependencies = [
name = "sysroot"
version = "0.0.0"
dependencies = [
- "alloc",
- "compiler_builtins",
- "core",
+ "proc_macro",
"std",
"test",
]
@@ -285,7 +384,6 @@ dependencies = [
"getopts",
"panic_abort",
"panic_unwind",
- "proc_macro",
"std",
]
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index 59ad80c32..fa3a10b9a 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-04-29"
-components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
+channel = "nightly-2023-06-15"
+components = ["rust-src", "rustc-dev", "llvm-tools"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
index e2db7d03a..99b97be24 100644
--- a/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/cargo-clif.rs
@@ -12,24 +12,33 @@ fn main() {
let mut rustflags = String::new();
rustflags.push_str(" -Cpanic=abort -Zpanic-abort-tests -Zcodegen-backend=");
- rustflags.push_str(
- sysroot
- .join(if cfg!(windows) { "bin" } else { "lib" })
- .join(
- env::consts::DLL_PREFIX.to_string()
- + "rustc_codegen_cranelift"
- + env::consts::DLL_SUFFIX,
- )
- .to_str()
- .unwrap(),
- );
+ if let Some(name) = option_env!("BUILTIN_BACKEND") {
+ rustflags.push_str(name);
+ } else {
+ rustflags.push_str(
+ sysroot
+ .join(if cfg!(windows) { "bin" } else { "lib" })
+ .join(
+ env::consts::DLL_PREFIX.to_string()
+ + "rustc_codegen_cranelift"
+ + env::consts::DLL_SUFFIX,
+ )
+ .to_str()
+ .unwrap(),
+ );
+ }
rustflags.push_str(" --sysroot ");
rustflags.push_str(sysroot.to_str().unwrap());
env::set_var("RUSTFLAGS", env::var("RUSTFLAGS").unwrap_or(String::new()) + &rustflags);
env::set_var("RUSTDOCFLAGS", env::var("RUSTDOCFLAGS").unwrap_or(String::new()) + &rustflags);
- // Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
+ let cargo = if let Some(cargo) = option_env!("CARGO") {
+ cargo
+ } else {
+ // Ensure that the right toolchain is used
+ env::set_var("RUSTUP_TOOLCHAIN", option_env!("TOOLCHAIN_NAME").expect("TOOLCHAIN_NAME"));
+ "cargo"
+ };
let args: Vec<_> = match env::args().nth(1).as_deref() {
Some("jit") => {
@@ -64,10 +73,10 @@ fn main() {
};
#[cfg(unix)]
- panic!("Failed to spawn cargo: {}", Command::new("cargo").args(args).exec());
+ panic!("Failed to spawn cargo: {}", Command::new(cargo).args(args).exec());
#[cfg(not(unix))]
std::process::exit(
- Command::new("cargo").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
+ Command::new(cargo).args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
);
}
diff --git a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs
index ab496a4a6..33d51bddd 100644
--- a/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/rustc-clif.rs
@@ -19,23 +19,34 @@ fn main() {
let mut args = vec![];
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
- let mut codegen_backend_arg = OsString::from("-Zcodegen-backend=");
- codegen_backend_arg.push(cg_clif_dylib_path);
- args.push(codegen_backend_arg);
- if !passed_args.contains(&OsString::from("--sysroot")) {
+ if let Some(name) = option_env!("BUILTIN_BACKEND") {
+ args.push(OsString::from(format!("-Zcodegen-backend={name}")))
+ } else {
+ let mut codegen_backend_arg = OsString::from("-Zcodegen-backend=");
+ codegen_backend_arg.push(cg_clif_dylib_path);
+ args.push(codegen_backend_arg);
+ }
+ if !passed_args.iter().any(|arg| {
+ arg == "--sysroot" || arg.to_str().map(|s| s.starts_with("--sysroot=")) == Some(true)
+ }) {
args.push(OsString::from("--sysroot"));
args.push(OsString::from(sysroot.to_str().unwrap()));
}
args.extend(passed_args);
- // Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
+ let rustc = if let Some(rustc) = option_env!("RUSTC") {
+ rustc
+ } else {
+ // Ensure that the right toolchain is used
+ env::set_var("RUSTUP_TOOLCHAIN", option_env!("TOOLCHAIN_NAME").expect("TOOLCHAIN_NAME"));
+ "rustc"
+ };
#[cfg(unix)]
- panic!("Failed to spawn rustc: {}", Command::new("rustc").args(args).exec());
+ panic!("Failed to spawn rustc: {}", Command::new(rustc).args(args).exec());
#[cfg(not(unix))]
std::process::exit(
- Command::new("rustc").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
+ Command::new(rustc).args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
);
}
diff --git a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs
index 545844446..10582cc7b 100644
--- a/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs
+++ b/compiler/rustc_codegen_cranelift/scripts/rustdoc-clif.rs
@@ -19,23 +19,34 @@ fn main() {
let mut args = vec![];
args.push(OsString::from("-Cpanic=abort"));
args.push(OsString::from("-Zpanic-abort-tests"));
- let mut codegen_backend_arg = OsString::from("-Zcodegen-backend=");
- codegen_backend_arg.push(cg_clif_dylib_path);
- args.push(codegen_backend_arg);
- if !passed_args.contains(&OsString::from("--sysroot")) {
+ if let Some(name) = option_env!("BUILTIN_BACKEND") {
+ args.push(OsString::from(format!("-Zcodegen-backend={name}")))
+ } else {
+ let mut codegen_backend_arg = OsString::from("-Zcodegen-backend=");
+ codegen_backend_arg.push(cg_clif_dylib_path);
+ args.push(codegen_backend_arg);
+ }
+ if !passed_args.iter().any(|arg| {
+ arg == "--sysroot" || arg.to_str().map(|s| s.starts_with("--sysroot=")) == Some(true)
+ }) {
args.push(OsString::from("--sysroot"));
args.push(OsString::from(sysroot.to_str().unwrap()));
}
args.extend(passed_args);
- // Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
+ let rustdoc = if let Some(rustdoc) = option_env!("RUSTDOC") {
+ rustdoc
+ } else {
+ // Ensure that the right toolchain is used
+ env::set_var("RUSTUP_TOOLCHAIN", option_env!("TOOLCHAIN_NAME").expect("TOOLCHAIN_NAME"));
+ "rustdoc"
+ };
#[cfg(unix)]
- panic!("Failed to spawn rustdoc: {}", Command::new("rustdoc").args(args).exec());
+ panic!("Failed to spawn rustdoc: {}", Command::new(rustdoc).args(args).exec());
#[cfg(not(unix))]
std::process::exit(
- Command::new("rustdoc").args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
+ Command::new(rustdoc).args(args).spawn().unwrap().wait().unwrap().code().unwrap_or(1),
);
}
diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh
index 3cbeb6375..e62788f2e 100755
--- a/compiler/rustc_codegen_cranelift/scripts/rustup.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh
@@ -32,12 +32,10 @@ case $1 in
./clean_all.sh
- ./y.rs prepare
-
- (cd download/sysroot && cargo update && cargo fetch && cp Cargo.lock ../../build_sysroot/)
+ ./y.sh prepare
;;
"commit")
- git add rust-toolchain build_sysroot/Cargo.lock
+ git add rust-toolchain
git commit -m "Rustup to $(rustc -V)"
;;
"push")
diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
index abb09775d..15b16b42b 100644
--- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -e
-./y.rs build --no-unstable-features
+./y.sh build --no-unstable-features
echo "[SETUP] Rust fork"
git clone https://github.com/rust-lang/rust.git || true
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 1329d3ea0..a7920cc54 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -10,12 +10,17 @@ pushd rust
command -v rg >/dev/null 2>&1 || cargo install ripgrep
-# FIXME add needs-asm-support to all tests in tests/ui/asm
rm -r tests/ui/{unsized-locals/,lto/,linkage*} || true
-for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" tests/{codegen-units,ui,incremental}); do
+for test in $(rg --files-with-matches "lto|// needs-asm-support" tests/{codegen-units,ui,incremental}); do
rm $test
done
+for test in tests/run-make/**/Makefile; do
+ if rg "# needs-asm-support" $test >/dev/null; then
+ rm -r $(dirname $test)
+ fi
+done
+
for test in $(rg -i --files-with-matches "//(\[\w+\])?~[^\|]*\s*ERR|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" tests/ui); do
rm $test
done
@@ -28,30 +33,20 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
# ================
# requires stack unwinding
-# FIXME add needs-unwind to these tests
-rm tests/incremental/change_crate_dep_kind.rs
-rm tests/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abort instead of exit(101)
-rm -r tests/run-make/c-unwind-abi-catch-lib-panic
-rm -r tests/run-make/c-unwind-abi-catch-panic
-rm -r tests/run-make/debug-assertions
-rm -r tests/run-make/foreign-double-unwind
-rm -r tests/run-make/foreign-exceptions
-rm -r tests/run-make/foreign-rust-exceptions
-rm -r tests/run-make/libtest-json
-rm -r tests/run-make/static-unwinding
-
-# requires compiling with -Cpanic=unwind
-rm -r tests/ui/macros/rfc-2011-nicer-assert-messages/
-rm -r tests/run-make/test-benches
-rm tests/ui/test-attrs/test-type.rs
-rm -r tests/run-make/const_fn_mir
-rm -r tests/run-make/intrinsic-unreachable
+# FIXME add needs-unwind to this test
+rm -r tests/run-make/libtest-junit
+
+# extra warning about -Cpanic=abort for proc macros
+rm tests/ui/proc-macro/crt-static.rs
+rm tests/ui/proc-macro/proc-macro-deprecated-attr.rs
+rm tests/ui/proc-macro/quote-debug.rs
+rm tests/ui/proc-macro/no-missing-docs.rs
+rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs
+rm tests/ui/proc-macro/allowed-signatures.rs
# vendor intrinsics
rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected
-rm tests/ui/intrinsics/const-eval-select-x86_64.rs # requires x86_64 vendor intrinsics
rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant"
-rm tests/ui/simd/intrinsic/float-math-pass.rs # simd_fcos unimplemented
# exotic linkages
rm tests/ui/issues/issue-33992.rs # unsupported linkages
@@ -85,6 +80,7 @@ rm -r tests/run-make/issue-64153
rm -r tests/run-make/codegen-options-parsing
rm -r tests/run-make/lto-*
rm -r tests/run-make/reproducible-build-2
+rm -r tests/run-make/issue-109934-lto-debuginfo
# optimization tests
# ==================
@@ -120,13 +116,8 @@ rm tests/ui/lint/lint-const-item-mutation.rs # same
rm tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs # same
rm tests/ui/suggestions/derive-trait-for-method-call.rs # same
rm tests/ui/typeck/issue-46112.rs # same
-
-rm tests/ui/proc-macro/crt-static.rs # extra warning about -Cpanic=abort for proc macros
-rm tests/ui/proc-macro/proc-macro-deprecated-attr.rs # same
-rm tests/ui/proc-macro/quote-debug.rs # same
-rm tests/ui/proc-macro/no-missing-docs.rs # same
-rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs # same
-rm tests/ui/proc-macro/allowed-signatures.rs # same
+rm tests/ui/consts/const_cmp_type_id.rs # same
+rm tests/ui/consts/issue-73976-monomorphic.rs # same
# rustdoc-clif passes extra args, suppressing the help message when no args are passed
rm -r tests/run-make/issue-88756-default-output
@@ -142,15 +133,15 @@ rm -r tests/ui/consts/missing_span_in_backtrace.rs # expects sysroot source to b
rm tests/incremental/spike-neg1.rs # errors out for some reason
rm tests/incremental/spike-neg2.rs # same
-rm tests/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors
-
-rm tests/ui/simd/simd-bitmask.rs # crash
+rm tests/ui/simd/simd-bitmask.rs # simd_bitmask doesn't implement [u*; N] return type
rm -r tests/run-make/issue-51671 # wrong filename given in case of --emit=obj
rm -r tests/run-make/issue-30063 # same
rm -r tests/run-make/multiple-emits # same
rm -r tests/run-make/output-type-permutations # same
rm -r tests/run-make/used # same
+rm -r tests/run-make/no-alloc-shim
+rm -r tests/run-make/emit-to-stdout
# bugs in the test suite
# ======================
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index 84e09cf0a..199fa6861 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -665,7 +665,8 @@ pub(crate) fn codegen_drop<'tcx>(
let arg_value = drop_place.place_ref(
fx,
- fx.layout_of(fx.tcx.mk_ref(
+ fx.layout_of(Ty::new_ref(
+ fx.tcx,
fx.tcx.lifetimes.re_erased,
TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut },
)),
diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs
index d4b1ae2b6..e92280b26 100644
--- a/compiler/rustc_codegen_cranelift/src/allocator.rs
+++ b/compiler/rustc_codegen_cranelift/src/allocator.rs
@@ -89,16 +89,16 @@ fn codegen_inner(
);
let data_id = module.declare_data(OomStrategy::SYMBOL, Linkage::Export, false, false).unwrap();
- let mut data_ctx = DataContext::new();
- data_ctx.set_align(1);
+ let mut data = DataDescription::new();
+ data.set_align(1);
let val = oom_strategy.should_panic();
- data_ctx.define(Box::new([val]));
- module.define_data(data_id, &data_ctx).unwrap();
+ data.define(Box::new([val]));
+ module.define_data(data_id, &data).unwrap();
let data_id =
module.declare_data(NO_ALLOC_SHIM_IS_UNSTABLE, Linkage::Export, false, false).unwrap();
- let mut data_ctx = DataContext::new();
- data_ctx.set_align(1);
- data_ctx.define(Box::new([0]));
- module.define_data(data_id, &data_ctx).unwrap();
+ let mut data = DataDescription::new();
+ data.set_align(1);
+ data.define(Box::new([0]));
+ module.define_data(data_id, &data).unwrap();
}
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index fcfa0b862..334b2780b 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -2,7 +2,7 @@
use rustc_ast::InlineAsmOptions;
use rustc_index::IndexVec;
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -156,6 +156,7 @@ pub(crate) fn compile_fn(
write!(clif, " {}", isa_flag).unwrap();
}
writeln!(clif, "\n").unwrap();
+ writeln!(clif, "; symbol {}", codegened_func.symbol_name).unwrap();
crate::PrintOnPanic(move || {
let mut clif = clif.clone();
::cranelift_codegen::write::decorate_function(
@@ -420,7 +421,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
target,
fn_span,
unwind: _,
- from_hir_call: _,
+ call_source: _,
} => {
fx.tcx.prof.generic_activity("codegen call").run(|| {
crate::abi::codegen_terminator_call(
@@ -570,7 +571,7 @@ fn codegen_stmt<'tcx>(
lval.write_cvalue(fx, res);
}
Rvalue::Cast(
- CastKind::Pointer(PointerCast::ReifyFnPointer),
+ CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer),
ref operand,
to_ty,
) => {
@@ -595,17 +596,17 @@ fn codegen_stmt<'tcx>(
}
}
Rvalue::Cast(
- CastKind::Pointer(PointerCast::UnsafeFnPointer),
+ CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer),
ref operand,
to_ty,
)
| Rvalue::Cast(
- CastKind::Pointer(PointerCast::MutToConstPointer),
+ CastKind::PointerCoercion(PointerCoercion::MutToConstPointer),
ref operand,
to_ty,
)
| Rvalue::Cast(
- CastKind::Pointer(PointerCast::ArrayToPointer),
+ CastKind::PointerCoercion(PointerCoercion::ArrayToPointer),
ref operand,
to_ty,
) => {
@@ -661,7 +662,7 @@ fn codegen_stmt<'tcx>(
}
}
Rvalue::Cast(
- CastKind::Pointer(PointerCast::ClosureFnPointer(_)),
+ CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
ref operand,
_to_ty,
) => {
@@ -683,7 +684,11 @@ fn codegen_stmt<'tcx>(
_ => bug!("{} cannot be cast to a fn ptr", operand.layout().ty),
}
}
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), ref operand, _to_ty) => {
+ Rvalue::Cast(
+ CastKind::PointerCoercion(PointerCoercion::Unsize),
+ ref operand,
+ _to_ty,
+ ) => {
let operand = codegen_operand(fx, operand);
crate::unsize::coerce_unsized_into(fx, operand, lval);
}
@@ -705,7 +710,6 @@ fn codegen_stmt<'tcx>(
let times = fx
.monomorphize(times)
.eval(fx.tcx, ParamEnv::reveal_all())
- .kind()
.try_to_bits(fx.tcx.data_layout.pointer_size)
.unwrap();
if operand.layout().size.bytes() == 0 {
@@ -746,7 +750,7 @@ fn codegen_stmt<'tcx>(
}
Rvalue::ShallowInitBox(ref operand, content_ty) => {
let content_ty = fx.monomorphize(content_ty);
- let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty));
+ let box_layout = fx.layout_of(Ty::new_box(fx.tcx, content_ty));
let operand = codegen_operand(fx, operand);
let operand = operand.load_scalar(fx);
lval.write_cvalue(fx, CValue::by_val(operand, box_layout));
@@ -887,7 +891,7 @@ pub(crate) fn codegen_place<'tcx>(
let ptr = cplace.to_ptr();
cplace = CPlace::for_ptr(
ptr.offset_i64(fx, elem_layout.size.bytes() as i64 * (from as i64)),
- fx.layout_of(fx.tcx.mk_array(*elem_ty, to - from)),
+ fx.layout_of(Ty::new_array(fx.tcx, *elem_ty, to - from)),
);
}
ty::Slice(elem_ty) => {
diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
index f751d8c17..b2bc289a5 100644
--- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
+++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs
@@ -22,8 +22,8 @@ pub(crate) fn maybe_codegen<'tcx>(
match bin_op {
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => None,
- BinOp::Add | BinOp::Sub => None,
- BinOp::Mul => {
+ BinOp::Add | BinOp::AddUnchecked | BinOp::Sub | BinOp::SubUnchecked => None,
+ BinOp::Mul | BinOp::MulUnchecked => {
let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)];
let ret_val = fx.lib_call(
"__multi3",
@@ -69,7 +69,7 @@ pub(crate) fn maybe_codegen<'tcx>(
}
}
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => None,
- BinOp::Shl | BinOp::Shr => None,
+ BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None,
}
}
@@ -92,7 +92,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
match bin_op {
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => unreachable!(),
BinOp::Mul if is_signed => {
- let out_ty = fx.tcx.mk_tup(&[lhs.layout().ty, fx.tcx.types.bool]);
+ let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32));
let lhs = lhs.load_scalar(fx);
let rhs = rhs.load_scalar(fx);
@@ -112,7 +112,7 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty)))
}
BinOp::Add | BinOp::Sub | BinOp::Mul => {
- let out_ty = fx.tcx.mk_tup(&[lhs.layout().ty, fx.tcx.types.bool]);
+ let out_ty = Ty::new_tup(fx.tcx, &[lhs.layout().ty, fx.tcx.types.bool]);
let out_place = CPlace::new_stack_slot(fx, fx.layout_of(out_ty));
let param_types = vec![
AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn),
@@ -131,9 +131,10 @@ pub(crate) fn maybe_codegen_checked<'tcx>(
fx.lib_call(name, param_types, vec![], &args);
Some(out_place.to_cvalue(fx))
}
+ BinOp::AddUnchecked | BinOp::SubUnchecked | BinOp::MulUnchecked => unreachable!(),
BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
BinOp::Div | BinOp::Rem => unreachable!(),
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => unreachable!(),
- BinOp::Shl | BinOp::Shr => unreachable!(),
+ BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => unreachable!(),
}
}
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 5eaa988dd..67ea20112 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -6,6 +6,7 @@ use rustc_index::IndexVec;
use rustc_middle::ty::layout::{
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers,
};
+use rustc_span::source_map::Spanned;
use rustc_span::SourceFile;
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::{Integer, Primitive};
@@ -98,7 +99,7 @@ fn clif_pair_type_from_ty<'tcx>(
/// 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 });
+ let ptr_ty = Ty::new_ptr(tcx, 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,
@@ -361,7 +362,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
self.instance.subst_mir_and_normalize_erasing_regions(
self.tcx,
ty::ParamEnv::reveal_all(),
- ty::EarlyBinder(value),
+ ty::EarlyBinder::bind(value),
)
}
@@ -454,12 +455,12 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
}
pub(crate) fn anonymous_str(&mut self, msg: &str) -> Value {
- let mut data_ctx = DataContext::new();
- data_ctx.define(msg.as_bytes().to_vec().into_boxed_slice());
+ let mut data = DataDescription::new();
+ data.define(msg.as_bytes().to_vec().into_boxed_slice());
let msg_id = self.module.declare_anonymous_data(false, false).unwrap();
// Ignore DuplicateDefinition error, as the data will be the same
- let _ = self.module.define_data(msg_id, &data_ctx);
+ let _ = self.module.define_data(msg_id, &data);
let local_msg_id = self.module.declare_data_in_func(msg_id, self.bcx.func);
if self.clif_comments.enabled() {
@@ -495,25 +496,16 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> {
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
- self.0.sess.span_fatal(span, err.to_string())
+ self.0.sess.emit_fatal(Spanned { span, node: err })
} else {
match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => {
- span_bug!(
- span,
- "`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
- sig,
- extra_args,
- err
- );
+ span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}");
}
FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!(
span,
- "`fn_abi_of_instance({}, {:?})` failed: {}",
- instance,
- extra_args,
- err
+ "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}"
);
}
}
diff --git a/compiler/rustc_codegen_cranelift/src/config.rs b/compiler/rustc_codegen_cranelift/src/config.rs
index 263401e1c..9e92d656c 100644
--- a/compiler/rustc_codegen_cranelift/src/config.rs
+++ b/compiler/rustc_codegen_cranelift/src/config.rs
@@ -82,6 +82,11 @@ impl BackendConfig {
let mut config = BackendConfig::default();
for opt in opts {
+ if opt.starts_with("-import-instr-limit") {
+ // Silently ignore -import-instr-limit. It is set by rust's build system even when
+ // testing cg_clif.
+ continue;
+ }
if let Some((name, value)) = opt.split_once('=') {
match name {
"mode" => config.codegen_mode = value.parse()?,
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 77af561a5..427340c33 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -324,12 +324,12 @@ fn data_id_for_static(
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) {
+ let mut data = DataDescription::new();
+ data.set_align(align);
+ let data_gv = module.declare_data_in_data(data_id, &mut data);
+ data.define(std::iter::repeat(0).take(pointer_ty(tcx).bytes() as usize).collect());
+ data.write_data_addr(0, data_gv, 0);
+ match module.define_data(ref_data_id, &data) {
// Every time the static is referenced there will be another definition of this global,
// so duplicate definitions are expected and allowed.
Err(ModuleError::DuplicateDefinition(_)) => {}
@@ -394,9 +394,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
continue;
}
- let mut data_ctx = DataContext::new();
+ let mut data = DataDescription::new();
let alloc = alloc.inner();
- data_ctx.set_align(alloc.align.bytes());
+ data.set_align(alloc.align.bytes());
if let Some(section_name) = section_name {
let (segment_name, section_name) = if tcx.sess.target.is_like_osx {
@@ -412,11 +412,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
} else {
("", section_name.as_str())
};
- data_ctx.set_segment_section(segment_name, section_name);
+ data.set_segment_section(segment_name, section_name);
}
let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
- data_ctx.define(bytes.into_boxed_slice());
+ data.define(bytes.into_boxed_slice());
for &(offset, alloc_id) in alloc.provenance().ptrs().iter() {
let addend = {
@@ -435,8 +435,8 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
assert_eq!(addend, 0);
let func_id =
crate::abi::import_function(tcx, module, instance.polymorphize(tcx));
- 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);
+ let local_func_id = module.declare_func_in_data(func_id, &mut data);
+ data.write_function_addr(offset.bytes() as u32, local_func_id);
continue;
}
GlobalAlloc::Memory(target_alloc) => {
@@ -462,11 +462,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
}
};
- 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);
+ let global_value = module.declare_data_in_data(data_id, &mut data);
+ data.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
}
- module.define_data(data_id, &data_ctx).unwrap();
+ module.define_data(data_id, &data).unwrap();
cx.done.insert(data_id);
}
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 463de6a91..1b454b666 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -81,7 +81,7 @@ impl DebugContext {
match tcx.sess.source_map().lookup_line(span.lo()) {
Ok(SourceFileAndLine { sf: file, line }) => {
- let line_pos = file.line_begin_pos(span.lo());
+ let line_pos = file.lines(|lines| lines[line]);
(
file,
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index aad9a9647..d143bcc96 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -54,8 +54,8 @@ impl OngoingCodegen {
self,
sess: &Session,
backend_config: &BackendConfig,
- ) -> (CodegenResults, FxHashMap<WorkProductId, WorkProduct>) {
- let mut work_products = FxHashMap::default();
+ ) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
+ let mut work_products = FxIndexMap::default();
let mut modules = vec![];
for module_codegen in self.modules {
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index 3118105a4..41e24acef 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -4,7 +4,7 @@
use std::cell::RefCell;
use std::ffi::CString;
use std::os::raw::{c_char, c_int};
-use std::sync::{mpsc, Mutex};
+use std::sync::{mpsc, Mutex, OnceLock};
use rustc_codegen_ssa::CrateInfo;
use rustc_middle::mir::mono::MonoItem;
@@ -13,9 +13,6 @@ use rustc_span::Symbol;
use cranelift_jit::{JITBuilder, JITModule};
-// FIXME use std::sync::OnceLock once it stabilizes
-use once_cell::sync::OnceCell;
-
use crate::{prelude::*, BackendConfig};
use crate::{CodegenCx, CodegenMode};
@@ -29,7 +26,7 @@ thread_local! {
}
/// The Sender owned by the rustc thread
-static GLOBAL_MESSAGE_SENDER: OnceCell<Mutex<mpsc::Sender<UnsafeMessage>>> = OnceCell::new();
+static GLOBAL_MESSAGE_SENDER: OnceLock<Mutex<mpsc::Sender<UnsafeMessage>>> = OnceLock::new();
/// A message that is sent from the jitted runtime to the rustc thread.
/// Senders are responsible for upholding `Send` semantics.
@@ -325,7 +322,7 @@ fn dep_symbol_lookup_fn(
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static => {
let name = crate_info.crate_name[&cnum];
- let mut err = sess.struct_err(&format!("Can't load static lib {}", name));
+ let mut err = sess.struct_err(format!("Can't load static lib {}", name));
err.note("rustc_codegen_cranelift can only load dylibs in JIT mode.");
err.emit();
}
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index 56d8f13ce..24ad0083a 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -19,7 +19,10 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
}
// Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8`
- "llvm.x86.sse2.pmovmskb.128" | "llvm.x86.avx2.pmovmskb" | "llvm.x86.sse2.movmsk.pd" => {
+ "llvm.x86.sse2.pmovmskb.128"
+ | "llvm.x86.avx2.pmovmskb"
+ | "llvm.x86.sse.movmsk.ps"
+ | "llvm.x86.sse2.movmsk.pd" => {
intrinsic_args!(fx, args => (a); intrinsic);
let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx);
@@ -107,16 +110,209 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
};
let a = codegen_operand(fx, a);
let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.sse2.pslli.d imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .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),
+ });
+ }
+ "llvm.x86.sse2.psrli.w" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ 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_ty, _res_lane_ty, lane| match imm8
.try_to_bits(Size::from_bytes(4))
.unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
{
+ imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
+ _ => fx.bcx.ins().iconst(types::I32, 0),
+ });
+ }
+ "llvm.x86.sse2.pslli.w" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.sse2.pslli.d imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .try_to_bits(Size::from_bytes(4))
+ .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+ {
+ imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
+ _ => fx.bcx.ins().iconst(types::I32, 0),
+ });
+ }
+ "llvm.x86.avx.psrli.d" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.avx.psrli.d imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .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),
+ });
+ }
+ "llvm.x86.avx.pslli.d" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.avx.pslli.d imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .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),
});
}
+ "llvm.x86.avx2.psrli.w" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.avx.psrli.w imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .try_to_bits(Size::from_bytes(4))
+ .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+ {
+ imm8 if imm8 < 16 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)),
+ _ => fx.bcx.ins().iconst(types::I32, 0),
+ });
+ }
+ "llvm.x86.avx2.pslli.w" => {
+ let (a, imm8) = match args {
+ [a, imm8] => (a, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8)
+ .expect("llvm.x86.avx.pslli.w imm8 not const");
+
+ simd_for_each_lane(fx, a, ret, &|fx, _lane_ty, _res_lane_ty, lane| match imm8
+ .try_to_bits(Size::from_bytes(4))
+ .unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8))
+ {
+ imm8 if imm8 < 16 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)),
+ _ => fx.bcx.ins().iconst(types::I32, 0),
+ });
+ }
+ "llvm.x86.ssse3.pshuf.b.128" | "llvm.x86.avx2.pshuf.b" => {
+ let (a, b) = match args {
+ [a, b] => (a, b),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let b = codegen_operand(fx, b);
+
+ // Based on the pseudocode at https://github.com/rust-lang/stdarch/blob/1cfbca8b38fd9b4282b2f054f61c6ca69fc7ce29/crates/core_arch/src/x86/avx2.rs#L2319-L2332
+ let zero = fx.bcx.ins().iconst(types::I8, 0);
+ for i in 0..16 {
+ let b_lane = b.value_lane(fx, i).load_scalar(fx);
+ let is_zero = fx.bcx.ins().band_imm(b_lane, 0x80);
+ let a_idx = fx.bcx.ins().band_imm(b_lane, 0xf);
+ let a_idx = fx.bcx.ins().uextend(fx.pointer_type, a_idx);
+ let a_lane = a.value_lane_dyn(fx, a_idx).load_scalar(fx);
+ let res = fx.bcx.ins().select(is_zero, zero, a_lane);
+ ret.place_lane(fx, i).to_ptr().store(fx, res, MemFlags::trusted());
+ }
+
+ if intrinsic == "llvm.x86.avx2.pshuf.b" {
+ for i in 16..32 {
+ let b_lane = b.value_lane(fx, i).load_scalar(fx);
+ let is_zero = fx.bcx.ins().band_imm(b_lane, 0x80);
+ let b_lane_masked = fx.bcx.ins().band_imm(b_lane, 0xf);
+ let a_idx = fx.bcx.ins().iadd_imm(b_lane_masked, 16);
+ let a_idx = fx.bcx.ins().uextend(fx.pointer_type, a_idx);
+ let a_lane = a.value_lane_dyn(fx, a_idx).load_scalar(fx);
+ let res = fx.bcx.ins().select(is_zero, zero, a_lane);
+ ret.place_lane(fx, i).to_ptr().store(fx, res, MemFlags::trusted());
+ }
+ }
+ }
+ "llvm.x86.avx2.vperm2i128" => {
+ // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_permute2x128_si256
+ let (a, b, imm8) = match args {
+ [a, b, imm8] => (a, b, imm8),
+ _ => bug!("wrong number of args for intrinsic {intrinsic}"),
+ };
+ let a = codegen_operand(fx, a);
+ let b = codegen_operand(fx, b);
+ let imm8 = codegen_operand(fx, imm8).load_scalar(fx);
+
+ let a_0 = a.value_lane(fx, 0).load_scalar(fx);
+ let a_1 = a.value_lane(fx, 1).load_scalar(fx);
+ let a_low = fx.bcx.ins().iconcat(a_0, a_1);
+ let a_2 = a.value_lane(fx, 2).load_scalar(fx);
+ let a_3 = a.value_lane(fx, 3).load_scalar(fx);
+ let a_high = fx.bcx.ins().iconcat(a_2, a_3);
+
+ let b_0 = b.value_lane(fx, 0).load_scalar(fx);
+ let b_1 = b.value_lane(fx, 1).load_scalar(fx);
+ let b_low = fx.bcx.ins().iconcat(b_0, b_1);
+ let b_2 = b.value_lane(fx, 2).load_scalar(fx);
+ let b_3 = b.value_lane(fx, 3).load_scalar(fx);
+ let b_high = fx.bcx.ins().iconcat(b_2, b_3);
+
+ fn select4(
+ fx: &mut FunctionCx<'_, '_, '_>,
+ a_high: Value,
+ a_low: Value,
+ b_high: Value,
+ b_low: Value,
+ control: Value,
+ ) -> Value {
+ let a_or_b = fx.bcx.ins().band_imm(control, 0b0010);
+ let high_or_low = fx.bcx.ins().band_imm(control, 0b0001);
+ let is_zero = fx.bcx.ins().band_imm(control, 0b1000);
+
+ let zero = fx.bcx.ins().iconst(types::I64, 0);
+ let zero = fx.bcx.ins().iconcat(zero, zero);
+
+ let res_a = fx.bcx.ins().select(high_or_low, a_high, a_low);
+ let res_b = fx.bcx.ins().select(high_or_low, b_high, b_low);
+ let res = fx.bcx.ins().select(a_or_b, res_b, res_a);
+ fx.bcx.ins().select(is_zero, zero, res)
+ }
+
+ let control0 = imm8;
+ let res_low = select4(fx, a_high, a_low, b_high, b_low, control0);
+ let (res_0, res_1) = fx.bcx.ins().isplit(res_low);
+
+ let control1 = fx.bcx.ins().ushr_imm(imm8, 4);
+ let res_high = select4(fx, a_high, a_low, b_high, b_low, control1);
+ let (res_2, res_3) = fx.bcx.ins().isplit(res_high);
+
+ ret.place_lane(fx, 0).to_ptr().store(fx, res_0, MemFlags::trusted());
+ ret.place_lane(fx, 1).to_ptr().store(fx, res_1, MemFlags::trusted());
+ ret.place_lane(fx, 2).to_ptr().store(fx, res_2, MemFlags::trusted());
+ ret.place_lane(fx, 3).to_ptr().store(fx, res_3, MemFlags::trusted());
+ }
"llvm.x86.sse2.storeu.dq" => {
intrinsic_args!(fx, args => (mem_addr, a); intrinsic);
let mem_addr = mem_addr.load_scalar(fx);
@@ -190,7 +386,7 @@ fn llvm_add_sub<'tcx>(
// carry0 | carry1 -> carry or borrow respectively
let cb_out = fx.bcx.ins().bor(cb0, cb1);
- let layout = fx.layout_of(fx.tcx.mk_tup(&[fx.tcx.types.u8, fx.tcx.types.u64]));
+ let layout = fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u8, fx.tcx.types.u64]));
let val = CValue::by_val_pair(cb_out, c, layout);
ret.write_cvalue(fx, val);
}
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 0a513b08b..5862f1829 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -472,28 +472,11 @@ fn codegen_regular_intrinsic_call<'tcx>(
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
}
- sym::unchecked_add
- | sym::unchecked_sub
- | sym::unchecked_mul
- | sym::unchecked_div
- | sym::exact_div
- | sym::unchecked_rem
- | sym::unchecked_shl
- | sym::unchecked_shr => {
+ sym::exact_div => {
intrinsic_args!(fx, args => (x, y); intrinsic);
- // FIXME trap on overflow
- let bin_op = match intrinsic {
- sym::unchecked_add => BinOp::Add,
- sym::unchecked_sub => BinOp::Sub,
- sym::unchecked_mul => BinOp::Mul,
- sym::unchecked_div | sym::exact_div => BinOp::Div,
- sym::unchecked_rem => BinOp::Rem,
- sym::unchecked_shl => BinOp::Shl,
- sym::unchecked_shr => BinOp::Shr,
- _ => unreachable!(),
- };
- let res = crate::num::codegen_int_binop(fx, bin_op, x, y);
+ // FIXME trap on inexact
+ let res = crate::num::codegen_int_binop(fx, BinOp::Div, x, y);
ret.write_cvalue(fx, res);
}
sym::saturating_add | sym::saturating_sub => {
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 5a038bfca..6741362e8 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -434,8 +434,36 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
- sym::simd_round => {
- intrinsic_args!(fx, args => (a); intrinsic);
+ sym::simd_fpow => {
+ intrinsic_args!(fx, args => (a, b); intrinsic);
+
+ if !a.layout().ty.is_simd() {
+ report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
+ return;
+ }
+
+ simd_pair_for_each_lane(fx, a, b, ret, &|fx, lane_ty, _ret_lane_ty, a_lane, b_lane| {
+ match lane_ty.kind() {
+ ty::Float(FloatTy::F32) => fx.lib_call(
+ "powf",
+ vec![AbiParam::new(types::F32), AbiParam::new(types::F32)],
+ vec![AbiParam::new(types::F32)],
+ &[a_lane, b_lane],
+ )[0],
+ ty::Float(FloatTy::F64) => fx.lib_call(
+ "pow",
+ vec![AbiParam::new(types::F64), AbiParam::new(types::F64)],
+ vec![AbiParam::new(types::F64)],
+ &[a_lane, b_lane],
+ )[0],
+ _ => unreachable!("{:?}", lane_ty),
+ }
+ });
+ }
+
+ sym::simd_fpowi => {
+ intrinsic_args!(fx, args => (a, exp); intrinsic);
+ let exp = exp.load_scalar(fx);
if !a.layout().ty.is_simd() {
report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
@@ -448,22 +476,71 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
ret,
&|fx, lane_ty, _ret_lane_ty, lane| match lane_ty.kind() {
ty::Float(FloatTy::F32) => fx.lib_call(
- "roundf",
+ "__powisf2", // compiler-builtins
+ vec![AbiParam::new(types::F32), AbiParam::new(types::I32)],
vec![AbiParam::new(types::F32)],
- vec![AbiParam::new(types::F32)],
- &[lane],
+ &[lane, exp],
)[0],
ty::Float(FloatTy::F64) => fx.lib_call(
- "round",
- vec![AbiParam::new(types::F64)],
+ "__powidf2", // compiler-builtins
+ vec![AbiParam::new(types::F64), AbiParam::new(types::I32)],
vec![AbiParam::new(types::F64)],
- &[lane],
+ &[lane, exp],
)[0],
_ => unreachable!("{:?}", lane_ty),
},
);
}
+ sym::simd_fsin
+ | sym::simd_fcos
+ | sym::simd_fexp
+ | sym::simd_fexp2
+ | sym::simd_flog
+ | sym::simd_flog10
+ | sym::simd_flog2
+ | sym::simd_round => {
+ intrinsic_args!(fx, args => (a); intrinsic);
+
+ if !a.layout().ty.is_simd() {
+ report_simd_type_validation_error(fx, intrinsic, span, a.layout().ty);
+ return;
+ }
+
+ simd_for_each_lane(fx, a, ret, &|fx, lane_ty, _ret_lane_ty, lane| {
+ let lane_ty = match lane_ty.kind() {
+ ty::Float(FloatTy::F32) => types::F32,
+ ty::Float(FloatTy::F64) => types::F64,
+ _ => unreachable!("{:?}", lane_ty),
+ };
+ let name = match (intrinsic, lane_ty) {
+ (sym::simd_fsin, types::F32) => "sinf",
+ (sym::simd_fsin, types::F64) => "sin",
+ (sym::simd_fcos, types::F32) => "cosf",
+ (sym::simd_fcos, types::F64) => "cos",
+ (sym::simd_fexp, types::F32) => "expf",
+ (sym::simd_fexp, types::F64) => "exp",
+ (sym::simd_fexp2, types::F32) => "exp2f",
+ (sym::simd_fexp2, types::F64) => "exp2",
+ (sym::simd_flog, types::F32) => "logf",
+ (sym::simd_flog, types::F64) => "log",
+ (sym::simd_flog10, types::F32) => "log10f",
+ (sym::simd_flog10, types::F64) => "log10",
+ (sym::simd_flog2, types::F32) => "log2f",
+ (sym::simd_flog2, types::F64) => "log2",
+ (sym::simd_round, types::F32) => "roundf",
+ (sym::simd_round, types::F64) => "round",
+ _ => unreachable!("{:?}", intrinsic),
+ };
+ fx.lib_call(
+ name,
+ vec![AbiParam::new(lane_ty)],
+ vec![AbiParam::new(lane_ty)],
+ &[lane],
+ )[0]
+ });
+ }
+
sym::simd_fabs | sym::simd_fsqrt | sym::simd_ceil | sym::simd_floor | sym::simd_trunc => {
intrinsic_args!(fx, args => (a); intrinsic);
@@ -488,7 +565,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
- sym::simd_reduce_add_ordered | sym::simd_reduce_add_unordered => {
+ sym::simd_reduce_add_ordered => {
intrinsic_args!(fx, args => (v, acc); intrinsic);
let acc = acc.load_scalar(fx);
@@ -507,7 +584,25 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
- sym::simd_reduce_mul_ordered | sym::simd_reduce_mul_unordered => {
+ sym::simd_reduce_add_unordered => {
+ intrinsic_args!(fx, args => (v); intrinsic);
+
+ // FIXME there must be no acc param for integer vectors
+ if !v.layout().ty.is_simd() {
+ report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
+ return;
+ }
+
+ simd_reduce(fx, v, None, ret, &|fx, lane_ty, a, b| {
+ if lane_ty.is_floating_point() {
+ fx.bcx.ins().fadd(a, b)
+ } else {
+ fx.bcx.ins().iadd(a, b)
+ }
+ });
+ }
+
+ sym::simd_reduce_mul_ordered => {
intrinsic_args!(fx, args => (v, acc); intrinsic);
let acc = acc.load_scalar(fx);
@@ -526,6 +621,24 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
+ sym::simd_reduce_mul_unordered => {
+ intrinsic_args!(fx, args => (v); intrinsic);
+
+ // FIXME there must be no acc param for integer vectors
+ if !v.layout().ty.is_simd() {
+ report_simd_type_validation_error(fx, intrinsic, span, v.layout().ty);
+ return;
+ }
+
+ simd_reduce(fx, v, None, ret, &|fx, lane_ty, a, b| {
+ if lane_ty.is_floating_point() {
+ fx.bcx.ins().fmul(a, b)
+ } else {
+ fx.bcx.ins().imul(a, b)
+ }
+ });
+ }
+
sym::simd_reduce_all => {
intrinsic_args!(fx, args => (v); intrinsic);
@@ -581,7 +694,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
simd_reduce(fx, v, None, ret, &|fx, _ty, a, b| fx.bcx.ins().bxor(a, b));
}
- sym::simd_reduce_min => {
+ sym::simd_reduce_min | sym::simd_reduce_min_nanless => {
intrinsic_args!(fx, args => (v); intrinsic);
if !v.layout().ty.is_simd() {
@@ -600,7 +713,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
});
}
- sym::simd_reduce_max => {
+ sym::simd_reduce_max | sym::simd_reduce_max_nanless => {
intrinsic_args!(fx, args => (v); intrinsic);
if !v.layout().ty.is_simd() {
@@ -878,6 +991,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
fx.tcx.sess.span_err(span, format!("Unknown SIMD intrinsic {}", intrinsic));
// Prevent verifier error
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
+ return;
}
}
let ret_block = fx.get_block(target);
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index 9966cc2ef..0de2dccda 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -88,7 +88,7 @@ mod prelude {
};
pub(crate) use rustc_target::abi::{Abi, FieldIdx, Scalar, Size, VariantIdx, FIRST_VARIANT};
- pub(crate) use rustc_data_structures::fx::FxHashMap;
+ pub(crate) use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
pub(crate) use rustc_index::Idx;
@@ -102,7 +102,7 @@ mod prelude {
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, FuncId, Linkage, Module};
+ pub(crate) use cranelift_module::{self, DataDescription, FuncId, Linkage, Module};
pub(crate) use crate::abi::*;
pub(crate) use crate::base::{codegen_operand, codegen_place};
@@ -223,7 +223,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
_outputs: &OutputFilenames,
- ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
+ ) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
Ok(ongoing_codegen
.downcast::<driver::aot::OngoingCodegen>()
.unwrap()
diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs
index ba53e01c7..8992f40fb 100644
--- a/compiler/rustc_codegen_cranelift/src/num.rs
+++ b/compiler/rustc_codegen_cranelift/src/num.rs
@@ -128,10 +128,11 @@ pub(crate) fn codegen_int_binop<'tcx>(
let rhs = in_rhs.load_scalar(fx);
let b = fx.bcx.ins();
+ // FIXME trap on overflow for the Unchecked versions
let val = match bin_op {
- BinOp::Add => b.iadd(lhs, rhs),
- BinOp::Sub => b.isub(lhs, rhs),
- BinOp::Mul => b.imul(lhs, rhs),
+ BinOp::Add | BinOp::AddUnchecked => b.iadd(lhs, rhs),
+ BinOp::Sub | BinOp::SubUnchecked => b.isub(lhs, rhs),
+ BinOp::Mul | BinOp::MulUnchecked => b.imul(lhs, rhs),
BinOp::Div => {
if signed {
b.sdiv(lhs, rhs)
@@ -149,16 +150,19 @@ pub(crate) fn codegen_int_binop<'tcx>(
BinOp::BitXor => b.bxor(lhs, rhs),
BinOp::BitAnd => b.band(lhs, rhs),
BinOp::BitOr => b.bor(lhs, rhs),
- BinOp::Shl => b.ishl(lhs, rhs),
- BinOp::Shr => {
+ BinOp::Shl | BinOp::ShlUnchecked => b.ishl(lhs, rhs),
+ BinOp::Shr | BinOp::ShrUnchecked => {
if signed {
b.sshr(lhs, rhs)
} else {
b.ushr(lhs, rhs)
}
}
+ BinOp::Offset => unreachable!("Offset is not an integer operation"),
// Compare binops handles by `codegen_binop`.
- _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty),
+ BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge => {
+ unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs.layout().ty, in_rhs.layout().ty);
+ }
};
CValue::by_val(val, in_lhs.layout())
@@ -266,7 +270,7 @@ pub(crate) fn codegen_checked_int_binop<'tcx>(
_ => bug!("binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", bin_op, in_lhs, in_rhs),
};
- let out_layout = fx.layout_of(fx.tcx.mk_tup(&[in_lhs.layout().ty, fx.tcx.types.bool]));
+ let out_layout = fx.layout_of(Ty::new_tup(fx.tcx, &[in_lhs.layout().ty, fx.tcx.types.bool]));
CValue::by_val_pair(res, has_overflow, out_layout)
}
diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
index 1007b33ec..5a4f9e804 100644
--- a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
+++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs
@@ -225,10 +225,10 @@ pub(crate) fn write_ir_file(
let res = std::fs::File::create(clif_file_name).and_then(|mut file| write(&mut file));
if let Err(err) = res {
// Using early_warn as no Session is available here
- rustc_session::early_warn(
+ let handler = rustc_session::EarlyErrorHandler::new(
rustc_session::config::ErrorOutputType::default(),
- format!("error writing ir file: {}", err),
);
+ handler.early_warn(format!("error writing ir file: {}", err));
}
}
diff --git a/compiler/rustc_codegen_cranelift/src/trap.rs b/compiler/rustc_codegen_cranelift/src/trap.rs
index 82a2ec579..2fb0c2164 100644
--- a/compiler/rustc_codegen_cranelift/src/trap.rs
+++ b/compiler/rustc_codegen_cranelift/src/trap.rs
@@ -30,5 +30,9 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) {
/// Trap code: user65535
pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, '_>, msg: impl AsRef<str>) {
codegen_print(fx, msg.as_ref());
+
+ let one = fx.bcx.ins().iconst(types::I32, 1);
+ fx.lib_call("exit", vec![AbiParam::new(types::I32)], vec![], &[one]);
+
fx.bcx.ins().trap(TrapCode::User(!0));
}
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index ff0e12410..6aeba13f6 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -1,6 +1,6 @@
-//! Codegen of the [`PointerCast::Unsize`] operation.
+//! Codegen of the [`PointerCoercion::Unsize`] operation.
//!
-//! [`PointerCast::Unsize`]: `rustc_middle::ty::adjustment::PointerCast::Unsize`
+//! [`PointerCoercion::Unsize`]: `rustc_middle::ty::adjustment::PointerCoercion::Unsize`
use crate::prelude::*;
diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
index b1fda6ff2..133c989b6 100644
--- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs
+++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs
@@ -258,6 +258,27 @@ impl<'tcx> CValue<'tcx> {
}
}
+ /// Like [`CValue::value_lane`] except allowing a dynamically calculated lane index.
+ pub(crate) fn value_lane_dyn(
+ self,
+ fx: &mut FunctionCx<'_, '_, 'tcx>,
+ lane_idx: Value,
+ ) -> CValue<'tcx> {
+ let layout = self.1;
+ assert!(layout.ty.is_simd());
+ let (_lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
+ let lane_layout = fx.layout_of(lane_ty);
+ match self.0 {
+ CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => unreachable!(),
+ CValueInner::ByRef(ptr, None) => {
+ let field_offset = fx.bcx.ins().imul_imm(lane_idx, lane_layout.size.bytes() as i64);
+ let field_ptr = ptr.offset_value(fx, field_offset);
+ CValue::by_ref(field_ptr, lane_layout)
+ }
+ CValueInner::ByRef(_, Some(_)) => unreachable!(),
+ }
+ }
+
/// If `ty` is signed, `const_val` must already be sign extended.
pub(crate) fn const_val(
fx: &mut FunctionCx<'_, '_, 'tcx>,
diff --git a/compiler/rustc_codegen_cranelift/test.sh b/compiler/rustc_codegen_cranelift/test.sh
index 13e778453..6357eebf0 100755
--- a/compiler/rustc_codegen_cranelift/test.sh
+++ b/compiler/rustc_codegen_cranelift/test.sh
@@ -1,2 +1,2 @@
#!/usr/bin/env bash
-exec ./y.rs test "$@"
+exec ./y.sh test "$@"
diff --git a/compiler/rustc_codegen_cranelift/y.rs b/compiler/rustc_codegen_cranelift/y.rs
index a68a10500..e806a64d9 100755
--- a/compiler/rustc_codegen_cranelift/y.rs
+++ b/compiler/rustc_codegen_cranelift/y.rs
@@ -1,35 +1,6 @@
#!/usr/bin/env bash
#![deny(unsafe_code)] /*This line is ignored by bash
# This block is ignored by rustc
-set -e
-echo "[BUILD] y.rs" 1>&2
-rustc $0 -o ${0/.rs/.bin} -Cdebuginfo=1 --edition 2021
-exec ${0/.rs/.bin} $@
+echo "Warning: y.rs is a deprecated alias for y.sh" 1>&2
+exec ./y.sh "$@"
*/
-
-#![warn(rust_2018_idioms)]
-#![warn(unused_lifetimes)]
-#![warn(unreachable_pub)]
-
-//! The build system for cg_clif
-//!
-//! # Manual compilation
-//!
-//! If your system doesn't support shell scripts you can manually compile and run this file using
-//! for example:
-//!
-//! ```shell
-//! $ rustc y.rs -o y.bin
-//! $ ./y.bin
-//! ```
-//!
-//! # Naming
-//!
-//! The name `y.rs` was chosen to not conflict with rustc's `x.py`.
-
-#[path = "build_system/mod.rs"]
-mod build_system;
-
-fn main() {
- build_system::main();
-}
diff --git a/compiler/rustc_codegen_cranelift/y.sh b/compiler/rustc_codegen_cranelift/y.sh
new file mode 100755
index 000000000..bc925a23e
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/y.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+set -e
+echo "[BUILD] build system" 1>&2
+rustc build_system/main.rs -o y.bin -Cdebuginfo=1 --edition 2021
+exec ./y.bin "$@"
diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
index 42fb35e73..556c64448 100644
--- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
+++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml
@@ -20,9 +20,9 @@ jobs:
matrix:
libgccjit_version:
- { gcc: "libgccjit.so", artifacts_branch: "master" }
- commands: [
- "--test-successful-rustc --nb-parts 2 --current-part 0",
- "--test-successful-rustc --nb-parts 2 --current-part 1",
+ cargo_runner: [
+ "sde -future -rtm_mode full --",
+ "",
]
steps:
@@ -36,6 +36,20 @@ jobs:
- name: Install packages
run: sudo apt-get install ninja-build ripgrep
+ - name: Install Intel Software Development Emulator
+ if: ${{ matrix.cargo_runner }}
+ run: |
+ mkdir intel-sde
+ cd intel-sde
+ dir=sde-external-9.14.0-2022-10-25-lin
+ file=$dir.tar.xz
+ wget https://downloadmirror.intel.com/751535/$file
+ tar xvf $file
+ sudo mkdir /usr/share/intel-sde
+ sudo cp -r $dir/* /usr/share/intel-sde
+ sudo ln -s /usr/share/intel-sde/sde /usr/bin/sde
+ sudo ln -s /usr/share/intel-sde/sde64 /usr/bin/sde64
+
- name: Download artifact
uses: dawidd6/action-download-artifact@v2
with:
@@ -91,6 +105,10 @@ jobs:
./prepare_build.sh
./build.sh --release --release-sysroot
cargo test
+
+ - name: Clean
+ if: ${{ !matrix.cargo_runner }}
+ run: |
./clean_all.sh
- name: Prepare dependencies
@@ -107,10 +125,18 @@ jobs:
args: --release
- name: Run tests
+ if: ${{ !matrix.cargo_runner }}
run: |
./test.sh --release --clean --release-sysroot --build-sysroot --mini-tests --std-tests --test-libcore
- name: Run stdarch tests
+ if: ${{ !matrix.cargo_runner }}
run: |
cd build_sysroot/sysroot_src/library/stdarch/
CHANNEL=release TARGET=x86_64-unknown-linux-gnu ../../../../cargo.sh test
+
+ - name: Run stdarch tests
+ if: ${{ matrix.cargo_runner }}
+ run: |
+ cd build_sysroot/sysroot_src/library/stdarch/
+ STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu ../../../../cargo.sh test -- --skip rtm --skip tbm --skip sse4a
diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock
index 0f2e152f8..1c8754bf6 100644
--- a/compiler/rustc_codegen_gcc/Cargo.lock
+++ b/compiler/rustc_codegen_gcc/Cargo.lock
@@ -35,7 +35,7 @@ dependencies = [
[[package]]
name = "gccjit"
version = "1.0.0"
-source = "git+https://github.com/antoyo/gccjit.rs#fe242b7eb26980e6c78859d51c8d4cc1e43381a3"
+source = "git+https://github.com/antoyo/gccjit.rs#d6e52626cfc6f487094a5d5ac66302baf3439984"
dependencies = [
"gccjit_sys",
]
@@ -43,7 +43,7 @@ dependencies = [
[[package]]
name = "gccjit_sys"
version = "0.0.1"
-source = "git+https://github.com/antoyo/gccjit.rs#fe242b7eb26980e6c78859d51c8d4cc1e43381a3"
+source = "git+https://github.com/antoyo/gccjit.rs#d6e52626cfc6f487094a5d5ac66302baf3439984"
dependencies = [
"libc",
]
diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md
index bb7419438..a93637d90 100644
--- a/compiler/rustc_codegen_gcc/Readme.md
+++ b/compiler/rustc_codegen_gcc/Readme.md
@@ -193,7 +193,7 @@ Using git-subtree with `rustc` requires a patched git to make it work.
The PR that is needed is [here](https://github.com/gitgitgadget/git/pull/493).
Use the following instructions to install it:
-```
+```bash
git clone git@github.com:tqc/git.git
cd git
git checkout tqc/subtree
@@ -204,6 +204,21 @@ make
cp git-subtree ~/bin
```
+Then, do a sync with this command:
+
+```bash
+PATH="$HOME/bin:$PATH" ~/bin/git-subtree push -P compiler/rustc_codegen_gcc/ ../rustc_codegen_gcc/ sync_branch_name
+cd ../rustc_codegen_gcc
+git checkout master
+git pull
+git checkout sync_branch_name
+git merge master
+```
+
+TODO: write a script that does the above.
+
+https://rust-lang.zulipchat.com/#narrow/stream/301329-t-devtools/topic/subtree.20madness/near/258877725
+
### How to use [mem-trace](https://github.com/antoyo/mem-trace)
`rustc` needs to be built without `jemalloc` so that `mem-trace` can overload `malloc` since `jemalloc` is linked statically, so a `LD_PRELOAD`-ed library won't a chance to intercept the calls to `malloc`.
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml b/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
index cfadf47cc..a84f86a82 100644
--- a/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
+++ b/compiler/rustc_codegen_gcc/build_sysroot/Cargo.toml
@@ -9,6 +9,7 @@ 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" }
+proc_macro = { path = "./sysroot_src/library/proc_macro" }
[patch.crates-io]
rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" }
diff --git a/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
index 56768bbf1..71b3876ba 100755
--- a/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
+++ b/compiler/rustc_codegen_gcc/build_sysroot/prepare_sysroot_src.sh
@@ -29,10 +29,10 @@ git config user.name || git config user.name "None"
git commit -m "Initial commit" -q
for file in $(ls ../../patches/ | grep -v patcha); do
-echo "[GIT] apply" $file
-git apply ../../patches/$file
-git add -A
-git commit --no-gpg-sign -m "Patch $file"
+ echo "[GIT] apply" $file
+ git apply ../../patches/$file
+ git add -A
+ git commit --no-gpg-sign -m "Patch $file"
done
popd
diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs
index 637b8dc53..0cd7e6047 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core.rs
@@ -451,6 +451,9 @@ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
drop_in_place(to_drop);
}
+#[lang = "unpin"]
+pub auto trait Unpin {}
+
#[lang = "deref"]
pub trait Deref {
type Target: ?Sized;
@@ -488,9 +491,23 @@ pub struct Box<T: ?Sized, A: Allocator = Global>(Unique<T>, A);
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
+impl<T> Box<T> {
+ pub fn new(val: T) -> Box<T> {
+ unsafe {
+ let size = intrinsics::size_of::<T>();
+ let ptr = libc::malloc(size);
+ intrinsics::copy(&val as *const T as *const u8, ptr, size);
+ Box(Unique { pointer: NonNull(ptr as *const T), _marker: PhantomData }, Global)
+ }
+ }
+}
+
impl<T: ?Sized, A: Allocator> Drop for Box<T, A> {
fn drop(&mut self) {
- // drop is currently performed by compiler.
+ // inner value is dropped by compiler.
+ unsafe {
+ libc::free(self.0.pointer.0 as *mut u8);
+ }
}
}
@@ -507,11 +524,6 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
libc::malloc(size)
}
-#[lang = "box_free"]
-unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, _alloc: ()) {
- libc::free(ptr.pointer.0 as *mut u8);
-}
-
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index cff260777..b93d68597 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -168,6 +168,9 @@ fn main() {
world as Box<dyn SomeTrait>;
assert_eq!(intrinsics::bitreverse(0b10101000u8), 0b00010101u8);
+ assert_eq!(intrinsics::bitreverse(0xddccu16), 0x33bbu16);
+ assert_eq!(intrinsics::bitreverse(0xffee_ddccu32), 0x33bb77ffu32);
+ assert_eq!(intrinsics::bitreverse(0x1234_5678_ffee_ddccu64), 0x33bb77ff1e6a2c48u64);
assert_eq!(intrinsics::bswap(0xabu8), 0xabu8);
assert_eq!(intrinsics::bswap(0xddccu16), 0xccddu16);
diff --git a/compiler/rustc_codegen_gcc/example/std_example.rs b/compiler/rustc_codegen_gcc/example/std_example.rs
index 5c171c49f..18f2ddcde 100644
--- a/compiler/rustc_codegen_gcc/example/std_example.rs
+++ b/compiler/rustc_codegen_gcc/example/std_example.rs
@@ -58,6 +58,7 @@ fn main() {
assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);
+ assert_eq!(0x1234_5678_ffee_ddcc_1234_5678_ffee_ddccu128.reverse_bits(), 0x33bb77ff1e6a2c4833bb77ff1e6a2c48u128);
let _d = 0i128.checked_div(2i128);
let _d = 0u128.checked_div(2u128);
diff --git a/compiler/rustc_codegen_gcc/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/failing-ui-tests.txt
index 8539e27ea..801464daa 100644
--- a/compiler/rustc_codegen_gcc/failing-ui-tests.txt
+++ b/compiler/rustc_codegen_gcc/failing-ui-tests.txt
@@ -54,8 +54,8 @@ tests/ui/issues/issue-40883.rs
tests/ui/issues/issue-43853.rs
tests/ui/issues/issue-47364.rs
tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs
-tests/ui/rfc-2091-track-caller/std-panic-locations.rs
-tests/ui/rfcs/rfc1857-drop-order.rs
+tests/ui/rfcs/rfc-2091-track-caller/std-panic-locations.rs
+tests/ui/rfcs/rfc-1857-stabilize-drop-order/drop-order.rs
tests/ui/simd/issue-17170.rs
tests/ui/simd/issue-39720.rs
tests/ui/simd/issue-89193.rs
@@ -66,3 +66,5 @@ tests/ui/generator/panic-safe.rs
tests/ui/issues/issue-14875.rs
tests/ui/issues/issue-29948.rs
tests/ui/panic-while-printing.rs
+tests/ui/enum-discriminant/get_discr.rs
+tests/ui/panics/nested_panic_caught.rs
diff --git a/compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch b/compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch
deleted file mode 100644
index ee5ba449f..000000000
--- a/compiler/rustc_codegen_gcc/patches/0023-core-Ignore-failing-tests.patch
+++ /dev/null
@@ -1,49 +0,0 @@
-From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001
-From: bjorn3 <bjorn3@users.noreply.github.com>
-Date: Sun, 24 Nov 2019 15:34:06 +0100
-Subject: [PATCH] [core] Ignore failing tests
-
----
- library/core/tests/iter.rs | 4 ++++
- library/core/tests/num/bignum.rs | 10 ++++++++++
- library/core/tests/num/mod.rs | 5 +++--
- library/core/tests/time.rs | 1 +
- 4 files changed, 18 insertions(+), 2 deletions(-)
-
-diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs
-index 4bc44e9..8e3c7a4 100644
---- a/library/core/tests/array.rs
-+++ b/library/core/tests/array.rs
-@@ -242,6 +242,7 @@ fn iterator_drops() {
- assert_eq!(i.get(), 5);
- }
-
-+/*
- // This test does not work on targets without panic=unwind support.
- // To work around this problem, test is marked is should_panic, so it will
- // be automagically skipped on unsuitable targets, such as
-@@ -283,6 +284,7 @@ fn array_default_impl_avoids_leaks_on_panic() {
- assert_eq!(COUNTER.load(Relaxed), 0);
- panic!("test succeeded")
- }
-+*/
-
- #[test]
- fn empty_array_is_always_default() {
-@@ -304,6 +304,7 @@ fn array_map() {
- assert_eq!(b, [1, 2, 3]);
- }
-
-+/*
- // See note on above test for why `should_panic` is used.
- #[test]
- #[should_panic(expected = "test succeeded")]
-@@ -332,6 +333,7 @@ fn array_map_drop_safety() {
- assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
- panic!("test succeeded")
- }
-+*/
-
- #[test]
- fn cell_allows_array_cycle() {
--- 2.21.0 (Apple Git-122)
diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain
index 933ecd45b..ebb04d006 100644
--- a/compiler/rustc_codegen_gcc/rust-toolchain
+++ b/compiler/rustc_codegen_gcc/rust-toolchain
@@ -1,3 +1,3 @@
[toolchain]
-channel = "nightly-2023-03-02"
+channel = "nightly-2023-06-19"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index 250aa79f8..4c3b7f503 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -518,7 +518,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place);
}
}
-
}
}
diff --git a/compiler/rustc_codegen_gcc/src/attributes.rs b/compiler/rustc_codegen_gcc/src/attributes.rs
index db841b1b5..eb0cce19b 100644
--- a/compiler/rustc_codegen_gcc/src/attributes.rs
+++ b/compiler/rustc_codegen_gcc/src/attributes.rs
@@ -2,9 +2,13 @@
use gccjit::FnAttribute;
use gccjit::Function;
use rustc_attr::InstructionSetAttr;
+#[cfg(feature="master")]
+use rustc_attr::InlineAttr;
use rustc_codegen_ssa::target_features::tied_target_features;
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty;
+#[cfg(feature="master")]
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_session::Session;
use rustc_span::symbol::sym;
use smallvec::{smallvec, SmallVec};
@@ -67,6 +71,24 @@ fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
}
}
+/// Get GCC attribute for the provided inline heuristic.
+#[cfg(feature="master")]
+#[inline]
+fn inline_attr<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, inline: InlineAttr) -> Option<FnAttribute<'gcc>> {
+ match inline {
+ InlineAttr::Hint => Some(FnAttribute::Inline),
+ InlineAttr::Always => Some(FnAttribute::AlwaysInline),
+ InlineAttr::Never => {
+ if cx.sess().target.arch != "amdgpu" {
+ Some(FnAttribute::NoInline)
+ } else {
+ None
+ }
+ }
+ InlineAttr::None => None,
+ }
+}
+
/// Composite function which sets GCC attributes for function depending on its AST (`#[attribute]`)
/// attributes.
pub fn from_fn_attrs<'gcc, 'tcx>(
@@ -77,6 +99,23 @@ pub fn from_fn_attrs<'gcc, 'tcx>(
) {
let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(instance.def_id());
+ #[cfg(feature="master")]
+ {
+ let inline =
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
+ InlineAttr::Never
+ }
+ else if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) {
+ InlineAttr::Hint
+ }
+ else {
+ codegen_fn_attrs.inline
+ };
+ if let Some(attr) = inline_attr(cx, inline) {
+ func.add_attribute(attr);
+ }
+ }
+
let function_features =
codegen_fn_attrs.target_features.iter().map(|features| features.as_str()).collect::<Vec<&str>>();
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 869344ce9..43d0aafbd 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -181,6 +181,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
})
.collect();
+ debug_assert_eq!(casted_args.len(), args.len());
+
Cow::Owned(casted_args)
}
@@ -207,7 +209,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let func_name = format!("{:?}", func_ptr);
- let casted_args: Vec<_> = param_types
+ let mut casted_args: Vec<_> = param_types
.into_iter()
.zip(args.iter())
.enumerate()
@@ -237,6 +239,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
})
.collect();
+ // NOTE: to take into account variadic functions.
+ for i in casted_args.len()..args.len() {
+ casted_args.push(args[i]);
+ }
+
Cow::Owned(casted_args)
}
@@ -280,8 +287,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
}
}
- fn function_ptr_call(&mut self, func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
- let gcc_func = func_ptr.get_type().dyncast_function_ptr_type().expect("function ptr");
+ fn function_ptr_call(&mut self, typ: Type<'gcc>, mut func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> {
+ let gcc_func =
+ match func_ptr.get_type().dyncast_function_ptr_type() {
+ Some(func) => func,
+ None => {
+ // NOTE: due to opaque pointers now being used, we need to cast here.
+ let new_func_type = typ.dyncast_function_ptr_type().expect("function ptr");
+ func_ptr = self.context.new_cast(None, func_ptr, typ);
+ new_func_type
+ },
+ };
let func_name = format!("{:?}", func_ptr);
let previous_arg_count = args.len();
let orig_args = args;
@@ -424,16 +440,17 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
self.llbb().end_with_void_return(None)
}
- fn ret(&mut self, value: RValue<'gcc>) {
- let value =
- if self.structs_as_pointer.borrow().contains(&value) {
- // NOTE: hack to workaround a limitation of the rustc API: see comment on
- // CodegenCx.structs_as_pointer
- value.dereference(None).to_rvalue()
- }
- else {
- value
- };
+ fn ret(&mut self, mut value: RValue<'gcc>) {
+ if self.structs_as_pointer.borrow().contains(&value) {
+ // NOTE: hack to workaround a limitation of the rustc API: see comment on
+ // CodegenCx.structs_as_pointer
+ value = value.dereference(None).to_rvalue();
+ }
+ let expected_return_type = self.current_func().get_return_type();
+ if !expected_return_type.is_compatible_with(value.get_type()) {
+ // NOTE: due to opaque pointers now being used, we need to cast here.
+ value = self.context.new_cast(None, value, expected_return_type);
+ }
self.llbb().end_with_return(None, value);
}
@@ -719,17 +736,25 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
unimplemented!();
}
- fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, _align: Align) -> RValue<'gcc> {
+ fn load(&mut self, pointee_ty: Type<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
let block = self.llbb();
let function = block.get_function();
// NOTE: instead of returning the dereference here, we have to assign it to a variable in
// the current basic block. Otherwise, it could be used in another basic block, causing a
// dereference after a drop, for instance.
- // TODO(antoyo): handle align of the load instruction.
- let ptr = self.context.new_cast(None, ptr, pointee_ty.make_pointer());
+ // FIXME(antoyo): this check that we don't call get_aligned() a second time on a type.
+ // Ideally, we shouldn't need to do this check.
+ let aligned_type =
+ if pointee_ty == self.cx.u128_type || pointee_ty == self.cx.i128_type {
+ pointee_ty
+ }
+ else {
+ pointee_ty.get_aligned(align.bytes())
+ };
+ let ptr = self.context.new_cast(None, ptr, aligned_type.make_pointer());
let deref = ptr.dereference(None).to_rvalue();
unsafe { RETURN_VALUE_COUNT += 1 };
- let loaded_value = function.new_local(None, pointee_ty, &format!("loadedValue{}", unsafe { RETURN_VALUE_COUNT }));
+ let loaded_value = function.new_local(None, aligned_type, &format!("loadedValue{}", unsafe { RETURN_VALUE_COUNT }));
block.add_assignment(None, loaded_value, deref);
loaded_value.to_rvalue()
}
@@ -758,7 +783,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
if place.layout.is_zst() {
- return OperandRef::new_zst(self, place.layout);
+ return OperandRef::zero_sized(place.layout);
}
fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) {
@@ -909,7 +934,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
self.context.new_bitcast(None, result, ptr_type)
}
- fn inbounds_gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+ fn inbounds_gep(&mut self, typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> {
+ // NOTE: due to opaque pointers now being used, we need to cast here.
+ let ptr = self.context.new_cast(None, ptr, typ.make_pointer());
// NOTE: array indexing is always considered in bounds in GCC (TODO(antoyo): to be verified).
let mut indices = indices.into_iter();
let index = indices.next().expect("first index in inbounds_gep");
@@ -938,6 +965,8 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
element.get_address(None)
}
else if let Some(struct_type) = value_type.is_struct() {
+ // NOTE: due to opaque pointers now being used, we need to bitcast here.
+ let ptr = self.bitcast_if_needed(ptr, value_type.make_pointer());
ptr.dereference_field(None, struct_type.get_field(idx as i32)).get_address(None)
}
else {
@@ -1356,7 +1385,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
fn call(
&mut self,
- _typ: Type<'gcc>,
+ typ: Type<'gcc>,
_fn_attrs: Option<&CodegenFnAttrs>,
fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>,
func: RValue<'gcc>,
@@ -1370,7 +1399,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
}
else {
// If it's a not function that was defined, it's a function pointer.
- self.function_ptr_call(func, args, funclet)
+ self.function_ptr_call(typ, func, args, funclet)
};
if let Some(_fn_abi) = fn_abi {
// TODO(bjorn3): Apply function attributes
@@ -1843,7 +1872,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
#[cfg(feature="master")]
let (cond, element_type) = {
- let then_val_vector_type = then_val.get_type().dyncast_vector().expect("vector type");
+ // TODO(antoyo): dyncast_vector should not require a call to unqualified.
+ let then_val_vector_type = then_val.get_type().unqualified().dyncast_vector().expect("vector type");
let then_val_element_type = then_val_vector_type.get_element_type();
let then_val_element_size = then_val_element_type.get_size();
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index ac04b61a3..b62f4676f 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -1,17 +1,15 @@
use gccjit::LValue;
use gccjit::{RValue, Type, ToRValue};
-use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::{
BaseTypeMethods,
ConstMethods,
- DerivedTypeMethods,
MiscMethods,
StaticMethods,
};
use rustc_middle::mir::Mutability;
-use rustc_middle::ty::layout::{TyAndLayout, LayoutOf};
+use rustc_middle::ty::layout::{LayoutOf};
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
-use rustc_target::abi::{self, HasDataLayout, Pointer, Size};
+use rustc_target::abi::{self, HasDataLayout, Pointer};
use crate::consts::const_alloc_to_gcc;
use crate::context::CodegenCx;
@@ -110,6 +108,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
self.const_uint(self.type_u64(), i)
}
+ fn const_u128(&self, i: u128) -> RValue<'gcc> {
+ self.const_uint_big(self.type_u128(), i)
+ }
+
fn const_usize(&self, i: u64) -> RValue<'gcc> {
let bit_size = self.data_layout().pointer_size.bits();
if bit_size < 64 {
@@ -240,27 +242,25 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
const_alloc_to_gcc(self, alloc)
}
- fn from_const_alloc(&self, layout: TyAndLayout<'tcx>, alloc: ConstAllocation<'tcx>, offset: Size) -> PlaceRef<'tcx, RValue<'gcc>> {
- assert_eq!(alloc.inner().align, layout.align.abi);
- let ty = self.type_ptr_to(layout.gcc_type(self));
- let value =
- if layout.size == Size::ZERO {
- let value = self.const_usize(alloc.inner().align.bytes());
- self.const_bitcast(value, ty)
- }
- else {
- let init = const_alloc_to_gcc(self, alloc);
- let base_addr = self.static_addr_of(init, alloc.inner().align, None);
+ fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> {
+ self.context.new_cast(None, val, ty)
+ }
- let array = self.const_bitcast(base_addr, self.type_i8p());
- let value = self.context.new_array_access(None, array, self.const_usize(offset.bytes())).get_address(None);
- self.const_bitcast(value, ty)
- };
- PlaceRef::new_sized(value, layout)
+ fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
+ if value.get_type() == self.bool_type.make_pointer() {
+ if let Some(pointee) = typ.get_pointee() {
+ if pointee.dyncast_vector().is_some() {
+ panic!()
+ }
+ }
+ }
+ // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
+ // SIMD builtins require a constant value.
+ self.bitcast_if_needed(value, typ)
}
- fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> {
- self.context.new_cast(None, val, ty)
+ fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
+ self.context.new_array_access(None, base_addr, self.const_usize(offset.bytes())).get_address(None)
}
}
diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs
index 792ab8f89..d8a1fd315 100644
--- a/compiler/rustc_codegen_gcc/src/consts.rs
+++ b/compiler/rustc_codegen_gcc/src/consts.rs
@@ -1,6 +1,6 @@
#[cfg(feature = "master")]
-use gccjit::FnAttribute;
-use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type};
+use gccjit::{FnAttribute, VarAttribute, Visibility};
+use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue};
use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods};
use rustc_middle::span_bug;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
@@ -16,21 +16,6 @@ use crate::context::CodegenCx;
use crate::errors::InvalidMinimumAlignment;
use crate::type_of::LayoutGccExt;
-impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
- pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
- if value.get_type() == self.bool_type.make_pointer() {
- if let Some(pointee) = typ.get_pointee() {
- if pointee.dyncast_vector().is_some() {
- panic!()
- }
- }
- }
- // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
- // SIMD builtins require a constant value.
- self.bitcast_if_needed(value, typ)
- }
-}
-
fn set_global_alignment<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, gv: LValue<'gcc>, mut align: Align) {
// The target may require greater alignment for globals than the type does.
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,
@@ -39,7 +24,7 @@ fn set_global_alignment<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, gv: LValue<'gcc>
match Align::from_bits(min) {
Ok(min) => align = align.max(min),
Err(err) => {
- cx.sess().emit_err(InvalidMinimumAlignment { err });
+ cx.sess().emit_err(InvalidMinimumAlignment { err: err.to_string() });
}
}
}
@@ -249,7 +234,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
);
if !self.tcx.is_reachable_non_generic(def_id) {
- // TODO(antoyo): set visibility.
+ #[cfg(feature = "master")]
+ global.add_attribute(VarAttribute::Visibility(Visibility::Hidden));
}
global
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 661681bdb..08507e196 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -477,7 +477,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) = err {
- self.sess().emit_fatal(respan(span, err))
+ self.sess().emit_fatal(respan(span, err.into_diagnostic()))
} else {
span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
}
@@ -499,21 +499,12 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
} else {
match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => {
- span_bug!(
- span,
- "`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
- sig,
- extra_args,
- err
- );
+ span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}");
}
FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!(
span,
- "`fn_abi_of_instance({}, {:?})` failed: {}",
- instance,
- extra_args,
- err
+ "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}"
);
}
}
diff --git a/compiler/rustc_codegen_gcc/src/coverageinfo.rs b/compiler/rustc_codegen_gcc/src/coverageinfo.rs
index 872fc2472..849e9886e 100644
--- a/compiler/rustc_codegen_gcc/src/coverageinfo.rs
+++ b/compiler/rustc_codegen_gcc/src/coverageinfo.rs
@@ -1,69 +1,11 @@
-use gccjit::RValue;
-use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods};
-use rustc_hir::def_id::DefId;
-use rustc_middle::mir::coverage::{
- CodeRegion,
- CounterValueReference,
- ExpressionOperandId,
- InjectedExpressionId,
- Op,
-};
+use rustc_codegen_ssa::traits::CoverageInfoBuilderMethods;
+use rustc_middle::mir::Coverage;
use rustc_middle::ty::Instance;
use crate::builder::Builder;
-use crate::context::CodegenCx;
impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
- fn set_function_source_hash(
- &mut self,
- _instance: Instance<'tcx>,
- _function_source_hash: u64,
- ) -> bool {
- unimplemented!();
- }
-
- fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool {
- // TODO(antoyo)
- false
- }
-
- fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option<CodeRegion>) -> bool {
- // TODO(antoyo)
- false
- }
-
- fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool {
+ fn add_coverage(&mut self, _instance: Instance<'tcx>, _coverage: &Coverage) {
// TODO(antoyo)
- false
- }
-}
-
-impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
- fn coverageinfo_finalize(&self) {
- // TODO(antoyo)
- }
-
- fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> {
- unimplemented!();
- }
-
- /// Functions with MIR-based coverage are normally codegenned _only_ if
- /// called. LLVM coverage tools typically expect every function to be
- /// defined (even if unused), with at least one call to LLVM intrinsic
- /// `instrprof.increment`.
- ///
- /// Codegen a small function that will never be called, with one counter
- /// that will never be incremented.
- ///
- /// For used/called functions, the coverageinfo was already added to the
- /// `function_coverage_map` (keyed by function `Instance`) during codegen.
- /// But in this case, since the unused function was _not_ previously
- /// codegenned, collect the coverage `CodeRegion`s from the MIR and add
- /// them. The first `CodeRegion` is used to add a single counter, with the
- /// same counter ID used in the injected `instrprof.increment` intrinsic
- /// call. Since the function is never called, all other `CodeRegion`s can be
- /// added as `unreachable_region`s.
- fn define_unused_fn(&self, _def_id: DefId) {
- unimplemented!();
}
}
diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs
index 4748e7e4b..493626c3c 100644
--- a/compiler/rustc_codegen_gcc/src/declare.rs
+++ b/compiler/rustc_codegen_gcc/src/declare.rs
@@ -132,7 +132,7 @@ fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*ll
pub fn mangle_name(name: &str) -> String {
name.replace(|char: char| {
if !char.is_alphanumeric() && char != '_' {
- debug_assert!("$.".contains(char), "Unsupported char in function name: {}", char);
+ debug_assert!("$.*".contains(char), "Unsupported char in function name {}: {}", name, char);
true
}
else {
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
index 8a4559355..438eab789 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/archs.rs
@@ -2967,10 +2967,6 @@ match name {
"llvm.nvvm.clz.ll" => "__nvvm_clz_ll",
"llvm.nvvm.cos.approx.f" => "__nvvm_cos_approx_f",
"llvm.nvvm.cos.approx.ftz.f" => "__nvvm_cos_approx_ftz_f",
- "llvm.nvvm.cp.async.ca.shared.global.16" => "__nvvm_cp_async_ca_shared_global_16",
- "llvm.nvvm.cp.async.ca.shared.global.4" => "__nvvm_cp_async_ca_shared_global_4",
- "llvm.nvvm.cp.async.ca.shared.global.8" => "__nvvm_cp_async_ca_shared_global_8",
- "llvm.nvvm.cp.async.cg.shared.global.16" => "__nvvm_cp_async_cg_shared_global_16",
"llvm.nvvm.cp.async.commit.group" => "__nvvm_cp_async_commit_group",
"llvm.nvvm.cp.async.mbarrier.arrive" => "__nvvm_cp_async_mbarrier_arrive",
"llvm.nvvm.cp.async.mbarrier.arrive.noinc" => "__nvvm_cp_async_mbarrier_arrive_noinc",
@@ -3086,18 +3082,8 @@ match name {
"llvm.nvvm.fma.rn.f16" => "__nvvm_fma_rn_f16",
"llvm.nvvm.fma.rn.f16x2" => "__nvvm_fma_rn_f16x2",
"llvm.nvvm.fma.rn.ftz.f" => "__nvvm_fma_rn_ftz_f",
- "llvm.nvvm.fma.rn.ftz.f16" => "__nvvm_fma_rn_ftz_f16",
- "llvm.nvvm.fma.rn.ftz.f16x2" => "__nvvm_fma_rn_ftz_f16x2",
- "llvm.nvvm.fma.rn.ftz.relu.f16" => "__nvvm_fma_rn_ftz_relu_f16",
- "llvm.nvvm.fma.rn.ftz.relu.f16x2" => "__nvvm_fma_rn_ftz_relu_f16x2",
- "llvm.nvvm.fma.rn.ftz.sat.f16" => "__nvvm_fma_rn_ftz_sat_f16",
- "llvm.nvvm.fma.rn.ftz.sat.f16x2" => "__nvvm_fma_rn_ftz_sat_f16x2",
"llvm.nvvm.fma.rn.relu.bf16" => "__nvvm_fma_rn_relu_bf16",
"llvm.nvvm.fma.rn.relu.bf16x2" => "__nvvm_fma_rn_relu_bf16x2",
- "llvm.nvvm.fma.rn.relu.f16" => "__nvvm_fma_rn_relu_f16",
- "llvm.nvvm.fma.rn.relu.f16x2" => "__nvvm_fma_rn_relu_f16x2",
- "llvm.nvvm.fma.rn.sat.f16" => "__nvvm_fma_rn_sat_f16",
- "llvm.nvvm.fma.rn.sat.f16x2" => "__nvvm_fma_rn_sat_f16x2",
"llvm.nvvm.fma.rp.d" => "__nvvm_fma_rp_d",
"llvm.nvvm.fma.rp.f" => "__nvvm_fma_rp_f",
"llvm.nvvm.fma.rp.ftz.f" => "__nvvm_fma_rp_ftz_f",
@@ -3111,32 +3097,18 @@ match name {
"llvm.nvvm.fmax.f16" => "__nvvm_fmax_f16",
"llvm.nvvm.fmax.f16x2" => "__nvvm_fmax_f16x2",
"llvm.nvvm.fmax.ftz.f" => "__nvvm_fmax_ftz_f",
- "llvm.nvvm.fmax.ftz.f16" => "__nvvm_fmax_ftz_f16",
- "llvm.nvvm.fmax.ftz.f16x2" => "__nvvm_fmax_ftz_f16x2",
"llvm.nvvm.fmax.ftz.nan.f" => "__nvvm_fmax_ftz_nan_f",
- "llvm.nvvm.fmax.ftz.nan.f16" => "__nvvm_fmax_ftz_nan_f16",
- "llvm.nvvm.fmax.ftz.nan.f16x2" => "__nvvm_fmax_ftz_nan_f16x2",
"llvm.nvvm.fmax.ftz.nan.xorsign.abs.f" => "__nvvm_fmax_ftz_nan_xorsign_abs_f",
- "llvm.nvvm.fmax.ftz.nan.xorsign.abs.f16" => "__nvvm_fmax_ftz_nan_xorsign_abs_f16",
- "llvm.nvvm.fmax.ftz.nan.xorsign.abs.f16x2" => "__nvvm_fmax_ftz_nan_xorsign_abs_f16x2",
"llvm.nvvm.fmax.ftz.xorsign.abs.f" => "__nvvm_fmax_ftz_xorsign_abs_f",
- "llvm.nvvm.fmax.ftz.xorsign.abs.f16" => "__nvvm_fmax_ftz_xorsign_abs_f16",
- "llvm.nvvm.fmax.ftz.xorsign.abs.f16x2" => "__nvvm_fmax_ftz_xorsign_abs_f16x2",
"llvm.nvvm.fmax.nan.bf16" => "__nvvm_fmax_nan_bf16",
"llvm.nvvm.fmax.nan.bf16x2" => "__nvvm_fmax_nan_bf16x2",
"llvm.nvvm.fmax.nan.f" => "__nvvm_fmax_nan_f",
- "llvm.nvvm.fmax.nan.f16" => "__nvvm_fmax_nan_f16",
- "llvm.nvvm.fmax.nan.f16x2" => "__nvvm_fmax_nan_f16x2",
"llvm.nvvm.fmax.nan.xorsign.abs.bf16" => "__nvvm_fmax_nan_xorsign_abs_bf16",
"llvm.nvvm.fmax.nan.xorsign.abs.bf16x2" => "__nvvm_fmax_nan_xorsign_abs_bf16x2",
"llvm.nvvm.fmax.nan.xorsign.abs.f" => "__nvvm_fmax_nan_xorsign_abs_f",
- "llvm.nvvm.fmax.nan.xorsign.abs.f16" => "__nvvm_fmax_nan_xorsign_abs_f16",
- "llvm.nvvm.fmax.nan.xorsign.abs.f16x2" => "__nvvm_fmax_nan_xorsign_abs_f16x2",
"llvm.nvvm.fmax.xorsign.abs.bf16" => "__nvvm_fmax_xorsign_abs_bf16",
"llvm.nvvm.fmax.xorsign.abs.bf16x2" => "__nvvm_fmax_xorsign_abs_bf16x2",
"llvm.nvvm.fmax.xorsign.abs.f" => "__nvvm_fmax_xorsign_abs_f",
- "llvm.nvvm.fmax.xorsign.abs.f16" => "__nvvm_fmax_xorsign_abs_f16",
- "llvm.nvvm.fmax.xorsign.abs.f16x2" => "__nvvm_fmax_xorsign_abs_f16x2",
"llvm.nvvm.fmin.bf16" => "__nvvm_fmin_bf16",
"llvm.nvvm.fmin.bf16x2" => "__nvvm_fmin_bf16x2",
"llvm.nvvm.fmin.d" => "__nvvm_fmin_d",
@@ -3144,32 +3116,18 @@ match name {
"llvm.nvvm.fmin.f16" => "__nvvm_fmin_f16",
"llvm.nvvm.fmin.f16x2" => "__nvvm_fmin_f16x2",
"llvm.nvvm.fmin.ftz.f" => "__nvvm_fmin_ftz_f",
- "llvm.nvvm.fmin.ftz.f16" => "__nvvm_fmin_ftz_f16",
- "llvm.nvvm.fmin.ftz.f16x2" => "__nvvm_fmin_ftz_f16x2",
"llvm.nvvm.fmin.ftz.nan.f" => "__nvvm_fmin_ftz_nan_f",
- "llvm.nvvm.fmin.ftz.nan.f16" => "__nvvm_fmin_ftz_nan_f16",
- "llvm.nvvm.fmin.ftz.nan.f16x2" => "__nvvm_fmin_ftz_nan_f16x2",
"llvm.nvvm.fmin.ftz.nan.xorsign.abs.f" => "__nvvm_fmin_ftz_nan_xorsign_abs_f",
- "llvm.nvvm.fmin.ftz.nan.xorsign.abs.f16" => "__nvvm_fmin_ftz_nan_xorsign_abs_f16",
- "llvm.nvvm.fmin.ftz.nan.xorsign.abs.f16x2" => "__nvvm_fmin_ftz_nan_xorsign_abs_f16x2",
"llvm.nvvm.fmin.ftz.xorsign.abs.f" => "__nvvm_fmin_ftz_xorsign_abs_f",
- "llvm.nvvm.fmin.ftz.xorsign.abs.f16" => "__nvvm_fmin_ftz_xorsign_abs_f16",
- "llvm.nvvm.fmin.ftz.xorsign.abs.f16x2" => "__nvvm_fmin_ftz_xorsign_abs_f16x2",
"llvm.nvvm.fmin.nan.bf16" => "__nvvm_fmin_nan_bf16",
"llvm.nvvm.fmin.nan.bf16x2" => "__nvvm_fmin_nan_bf16x2",
"llvm.nvvm.fmin.nan.f" => "__nvvm_fmin_nan_f",
- "llvm.nvvm.fmin.nan.f16" => "__nvvm_fmin_nan_f16",
- "llvm.nvvm.fmin.nan.f16x2" => "__nvvm_fmin_nan_f16x2",
"llvm.nvvm.fmin.nan.xorsign.abs.bf16" => "__nvvm_fmin_nan_xorsign_abs_bf16",
"llvm.nvvm.fmin.nan.xorsign.abs.bf16x2" => "__nvvm_fmin_nan_xorsign_abs_bf16x2",
"llvm.nvvm.fmin.nan.xorsign.abs.f" => "__nvvm_fmin_nan_xorsign_abs_f",
- "llvm.nvvm.fmin.nan.xorsign.abs.f16" => "__nvvm_fmin_nan_xorsign_abs_f16",
- "llvm.nvvm.fmin.nan.xorsign.abs.f16x2" => "__nvvm_fmin_nan_xorsign_abs_f16x2",
"llvm.nvvm.fmin.xorsign.abs.bf16" => "__nvvm_fmin_xorsign_abs_bf16",
"llvm.nvvm.fmin.xorsign.abs.bf16x2" => "__nvvm_fmin_xorsign_abs_bf16x2",
"llvm.nvvm.fmin.xorsign.abs.f" => "__nvvm_fmin_xorsign_abs_f",
- "llvm.nvvm.fmin.xorsign.abs.f16" => "__nvvm_fmin_xorsign_abs_f16",
- "llvm.nvvm.fmin.xorsign.abs.f16x2" => "__nvvm_fmin_xorsign_abs_f16x2",
"llvm.nvvm.fns" => "__nvvm_fns",
"llvm.nvvm.h2f" => "__nvvm_h2f",
"llvm.nvvm.i2d.rm" => "__nvvm_i2d_rm",
@@ -7895,6 +7853,10 @@ match name {
"llvm.x86.subborrow.u64" => "__builtin_ia32_subborrow_u64",
"llvm.x86.tbm.bextri.u32" => "__builtin_ia32_bextri_u32",
"llvm.x86.tbm.bextri.u64" => "__builtin_ia32_bextri_u64",
+ "llvm.x86.tcmmimfp16ps" => "__builtin_ia32_tcmmimfp16ps",
+ "llvm.x86.tcmmimfp16ps.internal" => "__builtin_ia32_tcmmimfp16ps_internal",
+ "llvm.x86.tcmmrlfp16ps" => "__builtin_ia32_tcmmrlfp16ps",
+ "llvm.x86.tcmmrlfp16ps.internal" => "__builtin_ia32_tcmmrlfp16ps_internal",
"llvm.x86.tdpbf16ps" => "__builtin_ia32_tdpbf16ps",
"llvm.x86.tdpbf16ps.internal" => "__builtin_ia32_tdpbf16ps_internal",
"llvm.x86.tdpbssd" => "__builtin_ia32_tdpbssd",
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
index 0edec566b..f28348380 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs
@@ -313,6 +313,13 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>(builder: &Builder<'a, 'gcc
let new_args = args.to_vec();
args = vec![new_args[1], new_args[0], new_args[2], new_args[3], new_args[4]].into();
},
+ "__builtin_ia32_vpshrdv_v8di" | "__builtin_ia32_vpshrdv_v4di" | "__builtin_ia32_vpshrdv_v2di" |
+ "__builtin_ia32_vpshrdv_v16si" | "__builtin_ia32_vpshrdv_v8si" | "__builtin_ia32_vpshrdv_v4si" |
+ "__builtin_ia32_vpshrdv_v32hi" | "__builtin_ia32_vpshrdv_v16hi" | "__builtin_ia32_vpshrdv_v8hi" => {
+ // The first two arguments are reversed, compared to LLVM.
+ let new_args = args.to_vec();
+ args = vec![new_args[1], new_args[0], new_args[2]].into();
+ },
_ => (),
}
}
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index 601768747..0b208be4e 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -551,141 +551,52 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
let context = &self.cx.context;
let result =
match width {
- 8 => {
- // First step.
- let left = self.and(value, context.new_rvalue_from_int(typ, 0xF0));
- let left = self.lshr(left, context.new_rvalue_from_int(typ, 4));
- let right = self.and(value, context.new_rvalue_from_int(typ, 0x0F));
- let right = self.shl(right, context.new_rvalue_from_int(typ, 4));
- let step1 = self.or(left, right);
-
- // Second step.
- let left = self.and(step1, context.new_rvalue_from_int(typ, 0xCC));
- let left = self.lshr(left, context.new_rvalue_from_int(typ, 2));
- let right = self.and(step1, context.new_rvalue_from_int(typ, 0x33));
- let right = self.shl(right, context.new_rvalue_from_int(typ, 2));
- let step2 = self.or(left, right);
-
- // Third step.
- let left = self.and(step2, context.new_rvalue_from_int(typ, 0xAA));
- let left = self.lshr(left, context.new_rvalue_from_int(typ, 1));
- let right = self.and(step2, context.new_rvalue_from_int(typ, 0x55));
- let right = self.shl(right, context.new_rvalue_from_int(typ, 1));
- let step3 = self.or(left, right);
-
- step3
- },
- 16 => {
- // First step.
- let left = self.and(value, context.new_rvalue_from_int(typ, 0x5555));
- let left = self.shl(left, context.new_rvalue_from_int(typ, 1));
- let right = self.and(value, context.new_rvalue_from_int(typ, 0xAAAA));
- let right = self.lshr(right, context.new_rvalue_from_int(typ, 1));
- let step1 = self.or(left, right);
-
- // Second step.
- let left = self.and(step1, context.new_rvalue_from_int(typ, 0x3333));
- let left = self.shl(left, context.new_rvalue_from_int(typ, 2));
- let right = self.and(step1, context.new_rvalue_from_int(typ, 0xCCCC));
- let right = self.lshr(right, context.new_rvalue_from_int(typ, 2));
- let step2 = self.or(left, right);
-
- // Third step.
- let left = self.and(step2, context.new_rvalue_from_int(typ, 0x0F0F));
- let left = self.shl(left, context.new_rvalue_from_int(typ, 4));
- let right = self.and(step2, context.new_rvalue_from_int(typ, 0xF0F0));
- let right = self.lshr(right, context.new_rvalue_from_int(typ, 4));
- let step3 = self.or(left, right);
-
- // Fourth step.
- let left = self.and(step3, context.new_rvalue_from_int(typ, 0x00FF));
- let left = self.shl(left, context.new_rvalue_from_int(typ, 8));
- let right = self.and(step3, context.new_rvalue_from_int(typ, 0xFF00));
- let right = self.lshr(right, context.new_rvalue_from_int(typ, 8));
- let step4 = self.or(left, right);
+ 8 | 16 | 32 | 64 => {
+ let mask = ((1u128 << width) - 1) as u64;
+ let (m0, m1, m2) = if width > 16 {
+ (
+ context.new_rvalue_from_long(typ, (0x5555555555555555u64 & mask) as i64),
+ context.new_rvalue_from_long(typ, (0x3333333333333333u64 & mask) as i64),
+ context.new_rvalue_from_long(typ, (0x0f0f0f0f0f0f0f0fu64 & mask) as i64),
+ )
+ } else {
+ (
+ context.new_rvalue_from_int(typ, (0x5555u64 & mask) as i32),
+ context.new_rvalue_from_int(typ, (0x3333u64 & mask) as i32),
+ context.new_rvalue_from_int(typ, (0x0f0fu64 & mask) as i32),
+ )
+ };
+ let one = context.new_rvalue_from_int(typ, 1);
+ let two = context.new_rvalue_from_int(typ, 2);
+ let four = context.new_rvalue_from_int(typ, 4);
- step4
- },
- 32 => {
- // TODO(antoyo): Refactor with other implementations.
// First step.
- let left = self.and(value, context.new_rvalue_from_long(typ, 0x55555555));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 1));
- let right = self.and(value, context.new_rvalue_from_long(typ, 0xAAAAAAAA));
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 1));
+ let left = self.lshr(value, one);
+ let left = self.and(left, m0);
+ let right = self.and(value, m0);
+ let right = self.shl(right, one);
let step1 = self.or(left, right);
// Second step.
- let left = self.and(step1, context.new_rvalue_from_long(typ, 0x33333333));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 2));
- let right = self.and(step1, context.new_rvalue_from_long(typ, 0xCCCCCCCC));
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 2));
+ let left = self.lshr(step1, two);
+ let left = self.and(left, m1);
+ let right = self.and(step1, m1);
+ let right = self.shl(right, two);
let step2 = self.or(left, right);
// Third step.
- let left = self.and(step2, context.new_rvalue_from_long(typ, 0x0F0F0F0F));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 4));
- let right = self.and(step2, context.new_rvalue_from_long(typ, 0xF0F0F0F0));
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 4));
+ let left = self.lshr(step2, four);
+ let left = self.and(left, m2);
+ let right = self.and(step2, m2);
+ let right = self.shl(right, four);
let step3 = self.or(left, right);
// Fourth step.
- let left = self.and(step3, context.new_rvalue_from_long(typ, 0x00FF00FF));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 8));
- let right = self.and(step3, context.new_rvalue_from_long(typ, 0xFF00FF00));
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 8));
- let step4 = self.or(left, right);
-
- // Fifth step.
- let left = self.and(step4, context.new_rvalue_from_long(typ, 0x0000FFFF));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 16));
- let right = self.and(step4, context.new_rvalue_from_long(typ, 0xFFFF0000));
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 16));
- let step5 = self.or(left, right);
-
- step5
- },
- 64 => {
- // First step.
- let left = self.shl(value, context.new_rvalue_from_long(typ, 32));
- let right = self.lshr(value, context.new_rvalue_from_long(typ, 32));
- let step1 = self.or(left, right);
-
- // Second step.
- let left = self.and(step1, context.new_rvalue_from_long(typ, 0x0001FFFF0001FFFF));
- let left = self.shl(left, context.new_rvalue_from_long(typ, 15));
- let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO(antoyo): transmute the number instead?
- let right = self.lshr(right, context.new_rvalue_from_long(typ, 17));
- let step2 = self.or(left, right);
-
- // Third step.
- let left = self.lshr(step2, context.new_rvalue_from_long(typ, 10));
- let left = self.xor(step2, left);
- let temp = self.and(left, context.new_rvalue_from_long(typ, 0x003F801F003F801F));
-
- let left = self.shl(temp, context.new_rvalue_from_long(typ, 10));
- let left = self.or(temp, left);
- let step3 = self.xor(left, step2);
-
- // Fourth step.
- let left = self.lshr(step3, context.new_rvalue_from_long(typ, 4));
- let left = self.xor(step3, left);
- let temp = self.and(left, context.new_rvalue_from_long(typ, 0x0E0384210E038421));
-
- let left = self.shl(temp, context.new_rvalue_from_long(typ, 4));
- let left = self.or(temp, left);
- let step4 = self.xor(left, step3);
-
- // Fifth step.
- let left = self.lshr(step4, context.new_rvalue_from_long(typ, 2));
- let left = self.xor(step4, left);
- let temp = self.and(left, context.new_rvalue_from_long(typ, 0x2248884222488842));
-
- let left = self.shl(temp, context.new_rvalue_from_long(typ, 2));
- let left = self.or(temp, left);
- let step5 = self.xor(left, step4);
-
- step5
+ if width == 8 {
+ step3
+ } else {
+ self.gcc_bswap(step3, width)
+ }
},
128 => {
// TODO(antoyo): find a more efficient implementation?
@@ -1236,19 +1147,19 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(cx: &'a CodegenCx<'gcc, 'tcx>, codegen: &mut
// 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 i8p = Ty::new_mut_ptr(tcx,tcx.types.i8);
// `unsafe fn(*mut i8) -> ()`
- let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+ let try_fn_ty = Ty::new_fn_ptr(tcx,ty::Binder::dummy(tcx.mk_fn_sig(
iter::once(i8p),
- tcx.mk_unit(),
+ Ty::new_unit(tcx,),
false,
rustc_hir::Unsafety::Unsafe,
Abi::Rust,
)));
// `unsafe fn(*mut i8, *mut i8) -> ()`
- let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+ let catch_fn_ty = Ty::new_fn_ptr(tcx,ty::Binder::dummy(tcx.mk_fn_sig(
[i8p, i8p].iter().cloned(),
- tcx.mk_unit(),
+ Ty::new_unit(tcx,),
false,
rustc_hir::Unsafety::Unsafe,
Abi::Rust,
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
index b59c3a64f..9115cf971 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs
@@ -165,10 +165,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
InvalidMonomorphizationReturnIntegerType { span, name, ret_ty, out_ty }
);
+ let arg1 = args[0].immediate();
+ // NOTE: we get different vector types for the same vector type and libgccjit doesn't
+ // compare them as equal, so bitcast.
+ // FIXME(antoyo): allow comparing vector types as equal in libgccjit.
+ let arg2 = bx.context.new_bitcast(None, args[1].immediate(), arg1.get_type());
return Ok(compare_simd_types(
bx,
- args[0].immediate(),
- args[1].immediate(),
+ arg1,
+ arg2,
in_elem,
llret_ty,
cmp_op,
@@ -341,7 +346,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// endian and MSB-first for big endian.
let vector = args[0].immediate();
- let vector_type = vector.get_type().dyncast_vector().expect("vector type");
+ // TODO(antoyo): dyncast_vector should not require a call to unqualified.
+ let vector_type = vector.get_type().unqualified().dyncast_vector().expect("vector type");
let elem_type = vector_type.get_element_type();
let expected_int_bits = in_len.max(8);
@@ -848,7 +854,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
(true, true) => {
// Algorithm from: https://codereview.stackexchange.com/questions/115869/saturated-signed-addition
// TODO(antoyo): improve using conditional operators if possible.
- let arg_type = lhs.get_type();
+ // TODO(antoyo): dyncast_vector should not require a call to unqualified.
+ let arg_type = lhs.get_type().unqualified();
// TODO(antoyo): convert lhs and rhs to unsigned.
let sum = lhs + rhs;
let vector_type = arg_type.dyncast_vector().expect("vector type");
@@ -878,7 +885,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
res & cmp
},
(true, false) => {
- let arg_type = lhs.get_type();
+ // TODO(antoyo): dyncast_vector should not require a call to unqualified.
+ let arg_type = lhs.get_type().unqualified();
// TODO(antoyo): this uses the same algorithm from saturating add, but add the
// negative of the right operand. Find a proper subtraction algorithm.
let rhs = bx.context.new_unary_op(None, UnaryOp::Minus, arg_type, rhs);
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index 442ce0ea5..2a6b64278 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -75,7 +75,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig,
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
use rustc_codegen_ssa::target_features::supported_target_features;
use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_metadata::EncodedMetadata;
@@ -111,6 +111,8 @@ impl CodegenBackend for GccCodegenBackend {
}
fn init(&self, sess: &Session) {
+ #[cfg(feature="master")]
+ gccjit::set_global_personality_function_name(b"rust_eh_personality\0");
if sess.lto() != Lto::No {
sess.emit_warning(LTONotSupported {});
}
@@ -137,7 +139,7 @@ impl CodegenBackend for GccCodegenBackend {
Box::new(res)
}
- fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
+ fn join_codegen(&self, ongoing_codegen: Box<dyn Any>, sess: &Session, _outputs: &OutputFilenames) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
let (codegen_results, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<GccCodegenBackend>>()
.expect("Expected GccCodegenBackend's OngoingCodegen, found Box<Any>")
diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs
index 5df8c1a20..e0823888f 100644
--- a/compiler/rustc_codegen_gcc/src/type_of.rs
+++ b/compiler/rustc_codegen_gcc/src/type_of.rs
@@ -159,8 +159,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
fn is_gcc_immediate(&self) -> bool {
match self.abi {
Abi::Scalar(_) | Abi::Vector { .. } => true,
- Abi::ScalarPair(..) => false,
- Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
+ Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
}
}
@@ -284,7 +283,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> {
// only wide pointer boxes are handled as pointers
// thin pointer boxes with scalar allocators are handled by the general logic below
ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
- let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+ let ptr_ty = Ty::new_mut_ptr(cx.tcx,self.ty.boxed_ty());
return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate);
}
_ => {}
@@ -384,8 +383,8 @@ impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
unimplemented!();
}
- fn fn_decl_backend_type(&self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
- // FIXME(antoyo): return correct type.
- self.type_void()
+ fn fn_decl_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> {
+ let (return_type, param_types, variadic, _) = fn_abi.gcc_type(self);
+ self.context.new_function_pointer_type(None, return_type, &param_types, variadic)
}
}
diff --git a/compiler/rustc_codegen_gcc/test.sh b/compiler/rustc_codegen_gcc/test.sh
index 6139892ae..592997b8a 100755
--- a/compiler/rustc_codegen_gcc/test.sh
+++ b/compiler/rustc_codegen_gcc/test.sh
@@ -214,12 +214,14 @@ function setup_rustc() {
rm config.toml || true
cat > config.toml <<EOF
+changelog-seen = 2
+
[rust]
codegen-backends = []
deny-warnings = false
[build]
-cargo = "$(which cargo)"
+cargo = "$(rustup which cargo)"
local-rebuild = true
rustc = "$HOME/.rustup/toolchains/$rust_toolchain-$TARGET_TRIPLE/bin/rustc"
@@ -237,7 +239,7 @@ EOF
function asm_tests() {
setup_rustc
- echo "[TEST] rustc test suite"
+ echo "[TEST] rustc asm test suite"
RUSTC_ARGS="-Zpanic-abort-tests -Csymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort"
COMPILETEST_FORCE_STAGE0=1 ./x.py test --run always --stage 0 tests/assembly/asm --rustc-args "$RUSTC_ARGS"
}
@@ -338,6 +340,8 @@ function test_rustc() {
for test in $(rg -i --files-with-matches "//(\[\w+\])?~|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" tests/ui); do
rm $test
done
+ rm tests/ui/consts/const_cmp_type_id.rs
+ rm tests/ui/consts/issue-73976-monomorphic.rs
git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed
diff --git a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
index 6188924b0..83abe145e 100644
--- a/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
+++ b/compiler/rustc_codegen_gcc/tools/generate_intrinsics.py
@@ -3,7 +3,6 @@ import os
import re
import sys
import subprocess
-from os import walk
def run_command(command, cwd=None):
@@ -180,7 +179,7 @@ def update_intrinsics(llvm_path, llvmint, llvmint2):
intrinsics[arch].sort(key=lambda x: (x[0], x[2]))
out.write(' // {}\n'.format(arch))
for entry in intrinsics[arch]:
- if entry[2] == True: # if it is a duplicate
+ if entry[2] is True: # if it is a duplicate
out.write(' // [DUPLICATE]: "{}" => "{}",\n'.format(entry[0], entry[1]))
elif "_round_mask" in entry[1]:
out.write(' // [INVALID CONVERSION]: "{}" => "{}",\n'.format(entry[0], entry[1]))
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index 55622fdb2..de1622951 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -20,8 +20,12 @@ codegen_llvm_error_writing_def_file =
codegen_llvm_from_llvm_diag = {$message}
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
-codegen_llvm_invalid_minimum_alignment =
- invalid minimum global alignment: {$err}
+
+codegen_llvm_invalid_minimum_alignment_not_power_of_two =
+ invalid minimum global alignment: {$align} is not power of 2
+
+codegen_llvm_invalid_minimum_alignment_too_large =
+ invalid minimum global alignment: {$align} is too large
codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}"
codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err}
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 28be6d033..d221bad28 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -351,7 +351,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
continue;
}
PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
- let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty);
+ let ptr_ty = Ty::new_mut_ptr(cx.tcx, arg.layout.ty);
let ptr_layout = cx.layout_of(ptr_ty);
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 0, true));
llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 651d644eb..39275272e 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -1,7 +1,6 @@
//! Set and unset common attributes on LLVM values.
use rustc_codegen_ssa::traits::*;
-use rustc_data_structures::small_str::SmallStr;
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::{self, TyCtxt};
@@ -88,6 +87,9 @@ pub fn sanitize_attrs<'ll>(
attrs.push(llvm::AttributeKind::SanitizeMemTag.create_attr(cx.llcx));
}
+ if enabled.contains(SanitizerSet::SAFESTACK) {
+ attrs.push(llvm::AttributeKind::SanitizeSafeStack.create_attr(cx.llcx));
+ }
attrs
}
@@ -478,8 +480,8 @@ pub fn from_fn_attrs<'ll, 'tcx>(
let global_features = cx.tcx.global_backend_features(()).iter().map(|s| s.as_str());
let function_features = function_features.iter().map(|s| s.as_str());
- let target_features =
- global_features.chain(function_features).intersperse(",").collect::<SmallStr<1024>>();
+ let target_features: String =
+ global_features.chain(function_features).intersperse(",").collect();
if !target_features.is_empty() {
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "target-features", &target_features));
}
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 604f68eb6..d7dd98d79 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -1,4 +1,4 @@
-use crate::back::write::{self, save_temp_bitcode, DiagnosticHandlers};
+use crate::back::write::{self, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers};
use crate::errors::{
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
};
@@ -302,7 +302,13 @@ fn fat_lto(
// The linking steps below may produce errors and diagnostics within LLVM
// which we'd like to handle and print, so set up our diagnostic handlers
// (which get unregistered when they go out of scope below).
- let _handler = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
+ let _handler = DiagnosticHandlers::new(
+ cgcx,
+ diag_handler,
+ llcx,
+ &module,
+ CodegenDiagnosticsStage::LTO,
+ );
// For all other modules we codegened we'll need to link them into our own
// bitcode. All modules were codegened in their own LLVM context, however,
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index ca2eab28f..0f5e97544 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -268,6 +268,16 @@ pub(crate) fn save_temp_bitcode(
}
}
+/// In what context is a dignostic handler being attached to a codegen unit?
+pub enum CodegenDiagnosticsStage {
+ /// Prelink optimization stage.
+ Opt,
+ /// LTO/ThinLTO postlink optimization stage.
+ LTO,
+ /// Code generation.
+ Codegen,
+}
+
pub struct DiagnosticHandlers<'a> {
data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a Handler),
llcx: &'a llvm::Context,
@@ -279,6 +289,8 @@ impl<'a> DiagnosticHandlers<'a> {
cgcx: &'a CodegenContext<LlvmCodegenBackend>,
handler: &'a Handler,
llcx: &'a llvm::Context,
+ module: &ModuleCodegen<ModuleLlvm>,
+ stage: CodegenDiagnosticsStage,
) -> Self {
let remark_passes_all: bool;
let remark_passes: Vec<CString>;
@@ -295,6 +307,20 @@ impl<'a> DiagnosticHandlers<'a> {
};
let remark_passes: Vec<*const c_char> =
remark_passes.iter().map(|name: &CString| name.as_ptr()).collect();
+ let remark_file = cgcx
+ .remark_dir
+ .as_ref()
+ // Use the .opt.yaml file suffix, which is supported by LLVM's opt-viewer.
+ .map(|dir| {
+ let stage_suffix = match stage {
+ CodegenDiagnosticsStage::Codegen => "codegen",
+ CodegenDiagnosticsStage::Opt => "opt",
+ CodegenDiagnosticsStage::LTO => "lto",
+ };
+ dir.join(format!("{}.{stage_suffix}.opt.yaml", module.name))
+ })
+ .and_then(|dir| dir.to_str().and_then(|p| CString::new(p).ok()));
+
let data = Box::into_raw(Box::new((cgcx, handler)));
unsafe {
let old_handler = llvm::LLVMRustContextGetDiagnosticHandler(llcx);
@@ -305,6 +331,9 @@ impl<'a> DiagnosticHandlers<'a> {
remark_passes_all,
remark_passes.as_ptr(),
remark_passes.len(),
+ // The `as_ref()` is important here, otherwise the `CString` will be dropped
+ // too soon!
+ remark_file.as_ref().map(|dir| dir.as_ptr()).unwrap_or(std::ptr::null()),
);
DiagnosticHandlers { data, llcx, old_handler }
}
@@ -523,7 +552,8 @@ pub(crate) unsafe fn optimize(
let llmod = module.module_llvm.llmod();
let llcx = &*module.module_llvm.llcx;
- let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
+ let _handlers =
+ DiagnosticHandlers::new(cgcx, diag_handler, llcx, module, CodegenDiagnosticsStage::Opt);
let module_name = module.name.clone();
let module_name = Some(&module_name[..]);
@@ -582,7 +612,13 @@ pub(crate) unsafe fn codegen(
let tm = &*module.module_llvm.tm;
let module_name = module.name.clone();
let module_name = Some(&module_name[..]);
- let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx);
+ let _handlers = DiagnosticHandlers::new(
+ cgcx,
+ diag_handler,
+ llcx,
+ &module,
+ CodegenDiagnosticsStage::Codegen,
+ );
if cgcx.msvc_imps_needed {
create_msvc_imps(cgcx, llcx, llmod);
@@ -775,7 +811,6 @@ pub(crate) unsafe fn codegen(
}
record_llvm_cgu_instructions_stats(&cgcx.prof, llmod);
- drop(handlers);
}
// `.dwo` files are only emitted if:
@@ -875,14 +910,19 @@ unsafe fn embed_bitcode(
// passed though then these sections will show up in the final output.
// Additionally the flag that we need to set here is `SHF_EXCLUDE`.
//
+ // * XCOFF - AIX linker ignores content in .ipa and .info if no auxiliary
+ // symbol associated with these sections.
+ //
// Unfortunately, LLVM provides no way to set custom section flags. For ELF
// and COFF we emit the sections using module level inline assembly for that
// reason (see issue #90326 for historical background).
+ let is_aix = cgcx.opts.target_triple.triple().contains("-aix");
let is_apple = cgcx.opts.target_triple.triple().contains("-ios")
|| cgcx.opts.target_triple.triple().contains("-darwin")
|| cgcx.opts.target_triple.triple().contains("-tvos")
|| cgcx.opts.target_triple.triple().contains("-watchos");
if is_apple
+ || is_aix
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|| cgcx.opts.target_triple.triple().starts_with("asmjs")
{
@@ -895,7 +935,13 @@ unsafe fn embed_bitcode(
);
llvm::LLVMSetInitializer(llglobal, llconst);
- let section = if is_apple { "__LLVM,__bitcode\0" } else { ".llvmbc\0" };
+ let section = if is_apple {
+ "__LLVM,__bitcode\0"
+ } else if is_aix {
+ ".ipa\0"
+ } else {
+ ".llvmbc\0"
+ };
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
@@ -907,7 +953,13 @@ unsafe fn embed_bitcode(
"rustc.embedded.cmdline\0".as_ptr().cast(),
);
llvm::LLVMSetInitializer(llglobal, llconst);
- let section = if is_apple { "__LLVM,__cmdline\0" } else { ".llvmcmd\0" };
+ let section = if is_apple {
+ "__LLVM,__cmdline\0"
+ } else if is_aix {
+ ".info\0"
+ } else {
+ ".llvmcmd\0"
+ };
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
} else {
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 4d0bcd53d..d55992bf0 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -24,6 +24,7 @@ use rustc_span::Span;
use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions};
use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
+use smallvec::SmallVec;
use std::borrow::Cow;
use std::ffi::CStr;
use std::iter;
@@ -230,7 +231,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
let args = self.check_call("invoke", llty, llfn, args);
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
- let mut bundles = vec![funclet_bundle];
+ let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
+ if let Some(funclet_bundle) = funclet_bundle {
+ bundles.push(funclet_bundle);
+ }
// Emit CFI pointer type membership test
self.cfi_type_test(fn_attrs, fn_abi, llfn);
@@ -238,9 +242,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
- bundles.push(kcfi_bundle);
+ if let Some(kcfi_bundle) = kcfi_bundle {
+ bundles.push(kcfi_bundle);
+ }
- bundles.retain(|bundle| bundle.is_some());
let invoke = unsafe {
llvm::LLVMRustBuildInvoke(
self.llbuilder,
@@ -486,7 +491,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
assert_eq!(place.llextra.is_some(), place.layout.is_unsized());
if place.layout.is_zst() {
- return OperandRef::new_zst(self, place.layout);
+ return OperandRef::zero_sized(place.layout);
}
#[instrument(level = "trace", skip(bx))]
@@ -577,8 +582,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
) {
let zero = self.const_usize(0);
let count = self.const_usize(count);
- let start = dest.project_index(self, zero).llval;
- let end = dest.project_index(self, count).llval;
let header_bb = self.append_sibling_block("repeat_loop_header");
let body_bb = self.append_sibling_block("repeat_loop_body");
@@ -587,24 +590,18 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
self.br(header_bb);
let mut header_bx = Self::build(self.cx, header_bb);
- let current = header_bx.phi(self.val_ty(start), &[start], &[self.llbb()]);
+ let i = header_bx.phi(self.val_ty(zero), &[zero], &[self.llbb()]);
- let keep_going = header_bx.icmp(IntPredicate::IntNE, current, end);
+ let keep_going = header_bx.icmp(IntPredicate::IntULT, i, count);
header_bx.cond_br(keep_going, body_bb, next_bb);
let mut body_bx = Self::build(self.cx, body_bb);
- let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size);
- cg_elem
- .val
- .store(&mut body_bx, PlaceRef::new_sized_aligned(current, cg_elem.layout, align));
-
- let next = body_bx.inbounds_gep(
- self.backend_type(cg_elem.layout),
- current,
- &[self.const_usize(1)],
- );
+ let dest_elem = dest.project_index(&mut body_bx, i);
+ cg_elem.val.store(&mut body_bx, dest_elem);
+
+ let next = body_bx.unchecked_uadd(i, self.const_usize(1));
body_bx.br(header_bb);
- header_bx.add_incoming_to_phi(current, next, body_bb);
+ header_bx.add_incoming_to_phi(i, next, body_bb);
*self = Self::build(self.cx, next_bb);
}
@@ -1197,7 +1194,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
let args = self.check_call("call", llty, llfn, args);
let funclet_bundle = funclet.map(|funclet| funclet.bundle());
let funclet_bundle = funclet_bundle.as_ref().map(|b| &*b.raw);
- let mut bundles = vec![funclet_bundle];
+ let mut bundles: SmallVec<[_; 2]> = SmallVec::new();
+ if let Some(funclet_bundle) = funclet_bundle {
+ bundles.push(funclet_bundle);
+ }
// Emit CFI pointer type membership test
self.cfi_type_test(fn_attrs, fn_abi, llfn);
@@ -1205,9 +1205,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
// Emit KCFI operand bundle
let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn);
let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw);
- bundles.push(kcfi_bundle);
+ if let Some(kcfi_bundle) = kcfi_bundle {
+ bundles.push(kcfi_bundle);
+ }
- bundles.retain(|bundle| bundle.is_some());
let call = unsafe {
llvm::LLVMRustBuildCall(
self.llbuilder,
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 9127fba38..a2db59bd6 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -8,16 +8,15 @@ use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
use rustc_ast::Mutability;
-use rustc_codegen_ssa::mir::place::PlaceRef;
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
use rustc_hir::def_id::DefId;
use rustc_middle::bug;
use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar};
-use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::TyCtxt;
use rustc_session::cstore::{DllCallingConvention, DllImport, PeImportNameType};
-use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer, Size};
+use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer};
use rustc_target::spec::Target;
use libc::{c_char, c_uint};
@@ -169,6 +168,10 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
self.const_uint(self.type_i64(), i)
}
+ fn const_u128(&self, i: u128) -> &'ll Value {
+ self.const_uint_big(self.type_i128(), i)
+ }
+
fn const_usize(&self, i: u64) -> &'ll Value {
let bit_size = self.data_layout().pointer_size.bits();
if bit_size < 64 {
@@ -307,38 +310,24 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
const_alloc_to_llvm(self, alloc)
}
- fn from_const_alloc(
- &self,
- layout: TyAndLayout<'tcx>,
- alloc: ConstAllocation<'tcx>,
- offset: Size,
- ) -> PlaceRef<'tcx, &'ll Value> {
- let alloc_align = alloc.inner().align;
- assert_eq!(alloc_align, layout.align.abi);
- let llty = self.type_ptr_to(layout.llvm_type(self));
- let llval = if layout.size == Size::ZERO {
- let llval = self.const_usize(alloc_align.bytes());
- unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
- } else {
- let init = const_alloc_to_llvm(self, alloc);
- let base_addr = self.static_addr_of(init, alloc_align, None);
-
- let llval = unsafe {
- llvm::LLVMRustConstInBoundsGEP2(
- self.type_i8(),
- self.const_bitcast(base_addr, self.type_i8p()),
- &self.const_usize(offset.bytes()),
- 1,
- )
- };
- self.const_bitcast(llval, llty)
- };
- PlaceRef::new_sized(llval, layout)
- }
-
fn const_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
consts::ptrcast(val, ty)
}
+
+ fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
+ self.const_bitcast(val, ty)
+ }
+
+ fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
+ unsafe {
+ llvm::LLVMRustConstInBoundsGEP2(
+ self.type_i8(),
+ self.const_bitcast(base_addr, self.type_i8p()),
+ &self.const_usize(offset.bytes()),
+ 1,
+ )
+ }
+ }
}
/// Get the [LLVM type][Type] of a [`Value`].
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 940358acd..df52f50f8 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -1,7 +1,9 @@
use crate::base;
use crate::common::{self, CodegenCx};
use crate::debuginfo;
-use crate::errors::{InvalidMinimumAlignment, SymbolAlreadyDefined};
+use crate::errors::{
+ InvalidMinimumAlignmentNotPowerOfTwo, InvalidMinimumAlignmentTooLarge, SymbolAlreadyDefined,
+};
use crate::llvm::{self, True};
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
@@ -19,7 +21,9 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto;
-use rustc_target::abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange};
+use rustc_target::abi::{
+ Align, AlignFromBytesError, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
+};
use std::ops::Range;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
@@ -129,9 +133,14 @@ fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align:
if let Some(min) = cx.sess().target.min_global_align {
match Align::from_bits(min) {
Ok(min) => align = align.max(min),
- Err(err) => {
- cx.sess().emit_err(InvalidMinimumAlignment { err });
- }
+ Err(err) => match err {
+ AlignFromBytesError::NotPowerOfTwo(align) => {
+ cx.sess().emit_err(InvalidMinimumAlignmentNotPowerOfTwo { align });
+ }
+ AlignFromBytesError::TooLarge(align) => {
+ cx.sess().emit_err(InvalidMinimumAlignmentTooLarge { align });
+ }
+ },
}
}
unsafe {
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 83101a854..e1e0a4428 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -9,7 +9,7 @@ use crate::type_::Type;
use crate::value::Value;
use cstr::cstr;
-use rustc_codegen_ssa::base::wants_msvc_seh;
+use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh};
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
@@ -528,19 +528,28 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
if let Some(llpersonality) = self.eh_personality.get() {
return llpersonality;
}
+
+ let name = if wants_msvc_seh(self.sess()) {
+ Some("__CxxFrameHandler3")
+ } else if wants_wasm_eh(self.sess()) {
+ // LLVM specifically tests for the name of the personality function
+ // There is no need for this function to exist anywhere, it will
+ // not be called. However, its name has to be "__gxx_wasm_personality_v0"
+ // for native wasm exceptions.
+ Some("__gxx_wasm_personality_v0")
+ } else {
+ None
+ };
+
let tcx = self.tcx;
let llfn = match tcx.lang_items().eh_personality() {
- Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr(
+ Some(def_id) if name.is_none() => self.get_fn_addr(
ty::Instance::resolve(tcx, ty::ParamEnv::reveal_all(), def_id, ty::List::empty())
.unwrap()
.unwrap(),
),
_ => {
- let name = if wants_msvc_seh(self.sess()) {
- "__CxxFrameHandler3"
- } else {
- "rust_eh_personality"
- };
+ let name = name.unwrap_or("rust_eh_personality");
if let Some(llfn) = self.get_declared_value(name) {
llfn
} else {
@@ -658,6 +667,10 @@ impl<'ll> CodegenCx<'ll, '_> {
let t_f32 = self.type_f32();
let t_f64 = self.type_f64();
let t_metadata = self.type_metadata();
+ let t_token = self.type_token();
+
+ ifn!("llvm.wasm.get.exception", fn(t_token) -> i8p);
+ ifn!("llvm.wasm.get.ehselector", fn(t_token) -> t_i32);
ifn!("llvm.wasm.trunc.unsigned.i32.f32", fn(t_f32) -> t_i32);
ifn!("llvm.wasm.trunc.unsigned.i32.f64", fn(t_f64) -> t_i32);
@@ -969,9 +982,9 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) = err {
- self.sess().emit_fatal(Spanned { span, node: err })
+ self.sess().emit_fatal(Spanned { span, node: err.into_diagnostic() })
} else {
- span_bug!(span, "failed to get layout for `{}`: {}", ty, err)
+ span_bug!(span, "failed to get layout for `{ty}`: {err:?}")
}
}
}
@@ -991,21 +1004,12 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
} else {
match fn_abi_request {
FnAbiRequest::OfFnPtr { sig, extra_args } => {
- span_bug!(
- span,
- "`fn_abi_of_fn_ptr({}, {:?})` failed: {}",
- sig,
- extra_args,
- err
- );
+ span_bug!(span, "`fn_abi_of_fn_ptr({sig}, {extra_args:?})` failed: {err:?}",);
}
FnAbiRequest::OfInstance { instance, extra_args } => {
span_bug!(
span,
- "`fn_abi_of_instance({}, {:?})` failed: {}",
- instance,
- extra_args,
- err
+ "`fn_abi_of_instance({instance}, {extra_args:?})` failed: {err:?}",
);
}
}
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
index 1791ce4b3..1791ce4b3 100644
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
index e4da3b8de..06844afd6 100644
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs
@@ -1,6 +1,7 @@
pub use super::ffi::*;
use rustc_index::{IndexSlice, IndexVec};
+use rustc_middle::bug;
use rustc_middle::mir::coverage::{
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
InjectedExpressionIndex, MappedExpressionIndex, Op,
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
index 21a1ac348..a1ff2aa66 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
@@ -1,10 +1,10 @@
use crate::common::CodegenCx;
use crate::coverageinfo;
+use crate::coverageinfo::map_data::{Counter, CounterExpression};
use crate::llvm;
use llvm::coverageinfo::CounterMappingRegion;
-use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression};
-use rustc_codegen_ssa::traits::{ConstMethods, CoverageInfoMethods};
+use rustc_codegen_ssa::traits::ConstMethods;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index cd261293e..42fdbd786 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -3,13 +3,13 @@ use crate::llvm;
use crate::abi::Abi;
use crate::builder::Builder;
use crate::common::CodegenCx;
+use crate::coverageinfo::map_data::{CounterExpression, FunctionCoverage};
use libc::c_uint;
use llvm::coverageinfo::CounterMappingRegion;
-use rustc_codegen_ssa::coverageinfo::map::{CounterExpression, FunctionCoverage};
use rustc_codegen_ssa::traits::{
- BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, CoverageInfoMethods,
- MiscMethods, StaticMethods,
+ BaseTypeMethods, BuilderMethods, ConstMethods, CoverageInfoBuilderMethods, MiscMethods,
+ StaticMethods,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
@@ -17,16 +17,20 @@ use rustc_hir::def_id::DefId;
use rustc_llvm::RustString;
use rustc_middle::bug;
use rustc_middle::mir::coverage::{
- CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op,
+ CodeRegion, CounterValueReference, CoverageKind, ExpressionOperandId, InjectedExpressionId, Op,
};
+use rustc_middle::mir::Coverage;
use rustc_middle::ty;
-use rustc_middle::ty::layout::FnAbiOf;
+use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::Instance;
+use rustc_middle::ty::Ty;
use std::cell::RefCell;
use std::ffi::CString;
+mod ffi;
+pub(crate) mod map_data;
pub mod mapgen;
const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START;
@@ -53,11 +57,17 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
}
}
-impl<'ll, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
- fn coverageinfo_finalize(&self) {
+// These methods used to be part of trait `CoverageInfoMethods`, which no longer
+// exists after most coverage code was moved out of SSA.
+impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
+ pub(crate) fn coverageinfo_finalize(&self) {
mapgen::finalize(self)
}
+ /// For LLVM codegen, returns a function-specific `Value` for a global
+ /// string, to hold the function name passed to LLVM intrinsic
+ /// `instrprof.increment()`. The `Value` is only created once per instance.
+ /// Multiple invocations with the same instance return the same `Value`.
fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value {
if let Some(coverage_context) = self.coverage_context() {
debug!("getting pgo_func_name_var for instance={:?}", instance);
@@ -94,6 +104,54 @@ impl<'ll, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
+ fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
+ let bx = self;
+
+ let Coverage { kind, code_region } = coverage.clone();
+ match kind {
+ CoverageKind::Counter { function_source_hash, id } => {
+ if bx.set_function_source_hash(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(instance, id, code_region);
+ }
+
+ let coverageinfo = bx.tcx().coverageinfo(instance.def);
+
+ let fn_name = bx.get_pgo_func_name_var(instance);
+ let hash = bx.const_u64(function_source_hash);
+ let num_counters = bx.const_u32(coverageinfo.num_counters);
+ let index = bx.const_u32(id.zero_based_index());
+ 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_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
+ }
+ CoverageKind::Unreachable => {
+ bx.add_coverage_unreachable(
+ instance,
+ code_region.expect("unreachable regions always have code regions"),
+ );
+ }
+ }
+ }
+}
+
+// These methods used to be part of trait `CoverageInfoBuilderMethods`, but
+// after moving most coverage code out of SSA they are now just ordinary methods.
+impl<'tcx> Builder<'_, '_, 'tcx> {
+ /// 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 `-C instrument-coverage` is
+ /// not enabled (a coverage map is not being generated).
fn set_function_source_hash(
&mut self,
instance: Instance<'tcx>,
@@ -115,6 +173,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
}
}
+ /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage`
+ /// is not enabled (a coverage map is not being generated).
fn add_coverage_counter(
&mut self,
instance: Instance<'tcx>,
@@ -137,6 +197,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
}
}
+ /// Returns true if the expression was added to the coverage map; false if
+ /// `-C instrument-coverage` is not enabled (a coverage map is not being generated).
fn add_coverage_counter_expression(
&mut self,
instance: Instance<'tcx>,
@@ -163,6 +225,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
}
}
+ /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage`
+ /// is not enabled (a coverage map is not being generated).
fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool {
if let Some(coverage_context) = self.coverage_context() {
debug!(
@@ -199,8 +263,8 @@ fn declare_unused_fn<'tcx>(cx: &CodegenCx<'_, 'tcx>, def_id: DefId) -> Instance<
tcx.symbol_name(instance).name,
cx.fn_abi_of_fn_ptr(
ty::Binder::dummy(tcx.mk_fn_sig(
- [tcx.mk_unit()],
- tcx.mk_unit(),
+ [Ty::new_unit(tcx)],
+ Ty::new_unit(tcx),
false,
hir::Unsafety::Unsafe,
Abi::Rust,
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 3fff112a0..65cbd5edc 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs
@@ -65,10 +65,10 @@ fn make_mir_scope<'ll, 'tcx>(
debug_context.scopes[parent]
} else {
// The root is the function itself.
- let loc = cx.lookup_debug_loc(mir.span.lo());
+ let file = cx.sess().source_map().lookup_source_file(mir.span.lo());
debug_context.scopes[scope] = DebugScope {
- file_start_pos: loc.file.start_pos,
- file_end_pos: loc.file.end_pos,
+ file_start_pos: file.start_pos,
+ file_end_pos: file.end_pos,
..debug_context.scopes[scope]
};
instantiated.insert(scope);
@@ -93,7 +93,7 @@ fn make_mir_scope<'ll, 'tcx>(
let callee = cx.tcx.subst_and_normalize_erasing_regions(
instance.substs,
ty::ParamEnv::reveal_all(),
- ty::EarlyBinder(callee),
+ ty::EarlyBinder::bind(callee),
);
let callee_fn_abi = cx.fn_abi_of_instance(callee, ty::List::empty());
cx.dbg_scope_fn(callee, callee_fn_abi, None)
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index bd2fba126..d61400d3f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -168,7 +168,7 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
// a (fat) pointer. Make sure it is not called for e.g. `Box<T, NonZSTAllocator>`.
debug_assert_eq!(
cx.size_and_align_of(ptr_type),
- cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type))
+ cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type))
);
let pointee_type_di_node = type_di_node(cx, pointee_type);
@@ -223,8 +223,11 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>(
// at all and instead emit regular struct debuginfo for it. We just
// need to make sure that we don't break existing debuginfo consumers
// by doing that (at least not without a warning period).
- let layout_type =
- if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type };
+ let layout_type = if ptr_type.is_box() {
+ Ty::new_mut_ptr(cx.tcx, pointee_type)
+ } else {
+ ptr_type
+ };
let layout = cx.layout_of(layout_type);
let addr_field = layout.field(cx, abi::FAT_PTR_ADDR);
@@ -430,7 +433,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D
return existing_di_node;
}
- debug!("type_di_node: {:?}", t);
+ debug!("type_di_node: {:?} kind: {:?}", t, t.kind());
let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() {
ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
@@ -1034,7 +1037,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
build_field_di_node(
cx,
closure_or_generator_di_node,
- capture_name,
+ capture_name.as_str(),
cx.size_and_align_of(up_var_ty),
layout.fields.offset(index),
DIFlags::FlagZero,
@@ -1298,7 +1301,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
// All function pointers are described as opaque pointers. This could be improved in the future
// by describing them as actual function pointers.
- let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit);
+ let void_pointer_ty = Ty::new_imm_ptr(tcx, tcx.types.unit);
let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty);
let usize_di_node = type_di_node(cx, tcx.types.usize);
let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty);
@@ -1388,7 +1391,7 @@ fn vcall_visibility_metadata<'ll, 'tcx>(
let trait_def_id = trait_ref_self.def_id();
let trait_vis = cx.tcx.visibility(trait_def_id);
- let cgus = cx.sess().codegen_units();
+ let cgus = cx.sess().codegen_units().as_usize();
let single_cgu = cgus == 1;
let lto = cx.sess().lto();
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
index ecb0912d3..b2765ffc9 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -676,8 +676,7 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
_ => unreachable!(),
};
- let (generator_layout, state_specific_upvar_names) =
- cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
+ let generator_layout = cx.tcx.optimized_mir(generator_def_id).generator_layout().unwrap();
let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(generator_def_id);
let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx);
@@ -714,7 +713,6 @@ fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
generator_type_and_layout,
generator_type_di_node,
generator_layout,
- &state_specific_upvar_names,
&common_upvar_names,
);
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
index 9e0e847a1..8746ce0c5 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
@@ -6,7 +6,7 @@ use rustc_hir::def::CtorKind;
use rustc_index::IndexSlice;
use rustc_middle::{
bug,
- mir::{GeneratorLayout, GeneratorSavedLocal},
+ mir::GeneratorLayout,
ty::{
self,
layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
@@ -323,8 +323,7 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
generator_type_and_layout: TyAndLayout<'tcx>,
generator_type_di_node: &'ll DIType,
generator_layout: &GeneratorLayout<'tcx>,
- state_specific_upvar_names: &IndexSlice<GeneratorSavedLocal, Option<Symbol>>,
- common_upvar_names: &[String],
+ common_upvar_names: &IndexSlice<FieldIdx, Symbol>,
) -> &'ll DIType {
let variant_name = GeneratorSubsts::variant_name(variant_index);
let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
@@ -357,7 +356,7 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
.map(|field_index| {
let generator_saved_local = generator_layout.variant_fields[variant_index]
[FieldIdx::from_usize(field_index)];
- let field_name_maybe = state_specific_upvar_names[generator_saved_local];
+ let field_name_maybe = generator_layout.field_names[generator_saved_local];
let field_name = field_name_maybe
.as_ref()
.map(|s| Cow::from(s.as_str()))
@@ -380,12 +379,13 @@ pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
// Fields that are common to all states
let common_fields: SmallVec<_> = generator_substs
.prefix_tys()
+ .zip(common_upvar_names)
.enumerate()
- .map(|(index, upvar_ty)| {
+ .map(|(index, (upvar_ty, upvar_name))| {
build_field_di_node(
cx,
variant_struct_type_di_node,
- &common_upvar_names[index],
+ upvar_name.as_str(),
cx.size_and_align_of(upvar_ty),
generator_type_and_layout.fields.offset(index),
DIFlags::FlagZero,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
index 978141917..4d1cd6486 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -155,8 +155,8 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>(
DIFlags::FlagZero,
),
|cx, generator_type_di_node| {
- let (generator_layout, state_specific_upvar_names) =
- cx.tcx.generator_layout_and_saved_local_names(generator_def_id);
+ let generator_layout =
+ cx.tcx.optimized_mir(generator_def_id).generator_layout().unwrap();
let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else {
bug!(
@@ -195,7 +195,6 @@ pub(super) fn build_generator_di_node<'ll, 'tcx>(
generator_type_and_layout,
generator_type_di_node,
generator_layout,
- &state_specific_upvar_names,
&common_upvar_names,
),
source_info,
@@ -412,13 +411,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>(
enum_type_and_layout.size.bits(),
enum_type_and_layout.align.abi.bits() as u32,
Size::ZERO.bits(),
- discr_value.opt_single_val().map(|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.
- assert_eq!(value as u64 as u128, value);
- cx.const_u64(value as u64)
- }),
+ discr_value.opt_single_val().map(|value| cx.const_u128(value)),
DIFlags::FlagZero,
variant_member_info.variant_struct_type_di_node,
)
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index c3f0a0033..b924c771a 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -263,7 +263,7 @@ impl CodegenCx<'_, '_> {
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);
+ let line_pos = file.lines(|lines| lines[line]);
// Use 1-based indexing.
let line = (line + 1) as u32;
@@ -332,7 +332,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature)
};
- let mut name = String::new();
+ let mut name = String::with_capacity(64);
type_names::push_item_name(tcx, def_id, false, &mut name);
// Find the enclosing function, in case this is a closure.
@@ -454,7 +454,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
ty::Array(ct, _)
if (*ct == cx.tcx.types.u8) || cx.layout_of(*ct).is_zst() =>
{
- cx.tcx.mk_imm_ptr(*ct)
+ Ty::new_imm_ptr(cx.tcx, *ct)
}
_ => t,
};
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs
index d5ea48c31..fa61c7dde 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs
@@ -28,7 +28,7 @@ pub fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DISco
.map(|parent| item_namespace(cx, DefId { krate: def_id.krate, index: parent }));
let namespace_name_string = {
- let mut output = String::new();
+ let mut output = String::with_capacity(64);
type_names::push_item_name(cx.tcx, def_id, false, &mut output);
output
};
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
index 6bcd3e5bf..7be836386 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
@@ -82,8 +82,8 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
ty::Foreign(_) => {
// Assert that pointers to foreign types really are thin:
debug_assert_eq!(
- cx.size_of(cx.tcx.mk_imm_ptr(pointee_tail_ty)),
- cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8))
+ cx.size_of(Ty::new_imm_ptr(cx.tcx, pointee_tail_ty)),
+ cx.size_of(Ty::new_imm_ptr(cx.tcx, cx.tcx.types.u8))
);
None
}
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 6a9173ab4..44869ced1 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -50,9 +50,15 @@ pub(crate) struct SymbolAlreadyDefined<'a> {
}
#[derive(Diagnostic)]
-#[diag(codegen_llvm_invalid_minimum_alignment)]
-pub(crate) struct InvalidMinimumAlignment {
- pub err: String,
+#[diag(codegen_llvm_invalid_minimum_alignment_not_power_of_two)]
+pub(crate) struct InvalidMinimumAlignmentNotPowerOfTwo {
+ pub align: u64,
+}
+
+#[derive(Diagnostic)]
+#[diag(codegen_llvm_invalid_minimum_alignment_too_large)]
+pub(crate) struct InvalidMinimumAlignmentTooLarge {
+ pub align: u64,
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 4e28034a8..a254c86c2 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -7,7 +7,7 @@ use crate::type_of::LayoutLlvmExt;
use crate::va_arg::emit_va_arg;
use crate::value::Value;
-use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh};
+use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh};
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization};
use rustc_codegen_ssa::mir::operand::OperandRef;
@@ -452,6 +452,8 @@ fn try_intrinsic<'ll>(
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 wants_wasm_eh(bx.sess()) {
+ codegen_wasm_try(bx, try_func, data, catch_func, dest);
} else if bx.sess().target.os == "emscripten" {
codegen_emcc_try(bx, try_func, data, catch_func, dest);
} else {
@@ -610,6 +612,80 @@ fn codegen_msvc_try<'ll>(
bx.store(ret, dest, i32_align);
}
+// WASM's definition of the `rust_try` function.
+fn codegen_wasm_try<'ll>(
+ bx: &mut Builder<'_, 'll, '_>,
+ try_func: &'ll Value,
+ data: &'ll Value,
+ catch_func: &'ll Value,
+ dest: &'ll Value,
+) {
+ let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| {
+ bx.set_personality_fn(bx.eh_personality());
+
+ let normal = bx.append_sibling_block("normal");
+ let catchswitch = bx.append_sibling_block("catchswitch");
+ let catchpad = bx.append_sibling_block("catchpad");
+ let caught = bx.append_sibling_block("caught");
+
+ let try_func = llvm::get_param(bx.llfn(), 0);
+ let data = llvm::get_param(bx.llfn(), 1);
+ let catch_func = llvm::get_param(bx.llfn(), 2);
+
+ // We're generating an IR snippet that looks like:
+ //
+ // declare i32 @rust_try(%try_func, %data, %catch_func) {
+ // %slot = alloca i8*
+ // invoke %try_func(%data) to label %normal unwind label %catchswitch
+ //
+ // normal:
+ // ret i32 0
+ //
+ // catchswitch:
+ // %cs = catchswitch within none [%catchpad] unwind to caller
+ //
+ // catchpad:
+ // %tok = catchpad within %cs [null]
+ // %ptr = call @llvm.wasm.get.exception(token %tok)
+ // %sel = call @llvm.wasm.get.ehselector(token %tok)
+ // call %catch_func(%data, %ptr)
+ // catchret from %tok to label %caught
+ //
+ // caught:
+ // ret i32 1
+ // }
+ //
+ let try_func_ty = bx.type_func(&[bx.type_i8p()], bx.type_void());
+ bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None);
+
+ bx.switch_to_block(normal);
+ bx.ret(bx.const_i32(0));
+
+ bx.switch_to_block(catchswitch);
+ let cs = bx.catch_switch(None, None, &[catchpad]);
+
+ bx.switch_to_block(catchpad);
+ let null = bx.const_null(bx.type_i8p());
+ let funclet = bx.catch_pad(cs, &[null]);
+
+ let ptr = bx.call_intrinsic("llvm.wasm.get.exception", &[funclet.cleanuppad()]);
+ let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]);
+
+ let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void());
+ bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet));
+ bx.catch_ret(&funclet, caught);
+
+ bx.switch_to_block(caught);
+ bx.ret(bx.const_i32(1));
+ });
+
+ // Note that no invoke is used here because by definition this function
+ // can't panic (that's what it's catching).
+ let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None);
+ let i32_align = bx.tcx().data_layout.i32_align.abi;
+ bx.store(ret, dest, i32_align);
+}
+
// Definition of the standard `try` function for Rust using the GNU-like model
// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke`
// instructions).
@@ -797,23 +873,29 @@ 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 i8p = Ty::new_mut_ptr(tcx, tcx.types.i8);
// `unsafe fn(*mut i8) -> ()`
- let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
- [i8p],
- tcx.mk_unit(),
- false,
- hir::Unsafety::Unsafe,
- Abi::Rust,
- )));
+ let try_fn_ty = Ty::new_fn_ptr(
+ tcx,
+ ty::Binder::dummy(tcx.mk_fn_sig(
+ [i8p],
+ Ty::new_unit(tcx),
+ false,
+ hir::Unsafety::Unsafe,
+ Abi::Rust,
+ )),
+ );
// `unsafe fn(*mut i8, *mut i8) -> ()`
- let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
- [i8p, i8p],
- tcx.mk_unit(),
- false,
- hir::Unsafety::Unsafe,
- Abi::Rust,
- )));
+ let catch_fn_ty = Ty::new_fn_ptr(
+ tcx,
+ ty::Binder::dummy(tcx.mk_fn_sig(
+ [i8p, i8p],
+ Ty::new_unit(tcx),
+ false,
+ hir::Unsafety::Unsafe,
+ Abi::Rust,
+ )),
+ );
// `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(
[try_fn_ty, i8p, catch_fn_ty],
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 805843e58..24ba28bbc 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -33,7 +33,7 @@ use rustc_codegen_ssa::back::write::{
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::ModuleCodegen;
use rustc_codegen_ssa::{CodegenResults, CompiledModule};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, FatalError, Handler, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_metadata::EncodedMetadata;
@@ -355,7 +355,7 @@ impl CodegenBackend for LlvmCodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
- ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
+ ) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed> {
let (codegen_results, work_products) = ongoing_codegen
.downcast::<rustc_codegen_ssa::back::write::OngoingCodegen<LlvmCodegenBackend>>()
.expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box<Any>")
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index de93a64c0..3ad546b61 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1,7 +1,7 @@
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
-use rustc_codegen_ssa::coverageinfo::map as coverage_map;
+use crate::coverageinfo::map_data as coverage_map;
use super::debuginfo::{
DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator,
@@ -196,6 +196,7 @@ pub enum AttributeKind {
AllocSize = 37,
AllocatedPointer = 38,
AllocAlign = 39,
+ SanitizeSafeStack = 40,
}
/// LLVMIntPredicate
@@ -584,6 +585,16 @@ pub enum ThreadLocalMode {
LocalExec,
}
+/// LLVMRustTailCallKind
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub enum TailCallKind {
+ None,
+ Tail,
+ MustTail,
+ NoTail,
+}
+
/// LLVMRustChecksumKind
#[derive(Copy, Clone)]
#[repr(C)]
@@ -1070,6 +1081,7 @@ extern "C" {
// Operations on other types
pub fn LLVMVoidTypeInContext(C: &Context) -> &Type;
+ pub fn LLVMTokenTypeInContext(C: &Context) -> &Type;
pub fn LLVMMetadataTypeInContext(C: &Context) -> &Type;
// Operations on all values
@@ -1194,6 +1206,7 @@ extern "C" {
NameLen: size_t,
) -> Option<&Value>;
pub fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool);
+ pub fn LLVMRustSetTailCallKind(CallInst: &Value, TKC: TailCallKind);
// Operations on attributes
pub fn LLVMRustCreateAttrNoValue(C: &Context, attr: AttributeKind) -> &Attribute;
@@ -1300,7 +1313,7 @@ extern "C" {
NumArgs: c_uint,
Then: &'a BasicBlock,
Catch: &'a BasicBlock,
- OpBundles: *const Option<&OperandBundleDef<'a>>,
+ OpBundles: *const &OperandBundleDef<'a>,
NumOpBundles: c_uint,
Name: *const c_char,
) -> &'a Value;
@@ -1672,7 +1685,7 @@ extern "C" {
Fn: &'a Value,
Args: *const &'a Value,
NumArgs: c_uint,
- OpBundles: *const Option<&OperandBundleDef<'a>>,
+ OpBundles: *const &OperandBundleDef<'a>,
NumOpBundles: c_uint,
) -> &'a Value;
pub fn LLVMRustBuildMemCpy<'a>(
@@ -2511,6 +2524,7 @@ extern "C" {
remark_all_passes: bool,
remark_passes: *const *const c_char,
remark_passes_len: usize,
+ remark_file: *const c_char,
);
#[allow(improper_ctypes)]
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index d3fad5699..7e672a8dc 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -52,6 +52,10 @@ impl<'ll> CodegenCx<'ll, '_> {
unsafe { llvm::LLVMVoidTypeInContext(self.llcx) }
}
+ pub(crate) fn type_token(&self) -> &'ll Type {
+ unsafe { llvm::LLVMTokenTypeInContext(self.llcx) }
+ }
+
pub(crate) fn type_metadata(&self) -> &'ll Type {
unsafe { llvm::LLVMMetadataTypeInContext(self.llcx) }
}
@@ -288,6 +292,9 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
fn reg_backend_type(&self, ty: &Reg) -> &'ll Type {
ty.llvm_type(self)
}
+ fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option<Self::Type> {
+ layout.scalar_copy_llvm_type(self)
+ }
}
impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index e264ce78f..7f7da8483 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -6,6 +6,7 @@ use rustc_middle::bug;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
+use rustc_target::abi::HasDataLayout;
use rustc_target::abi::{Abi, Align, FieldsShape};
use rustc_target::abi::{Int, Pointer, F32, F64};
use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants};
@@ -192,14 +193,14 @@ pub trait LayoutLlvmExt<'tcx> {
) -> &'a Type;
fn llvm_field_index<'a>(&self, cx: &CodegenCx<'a, 'tcx>, index: usize) -> u64;
fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option<PointeeInfo>;
+ fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type>;
}
impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
fn is_llvm_immediate(&self) -> bool {
match self.abi {
Abi::Scalar(_) | Abi::Vector { .. } => true,
- Abi::ScalarPair(..) => false,
- Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(),
+ Abi::ScalarPair(..) | Abi::Uninhabited | Abi::Aggregate { .. } => false,
}
}
@@ -336,12 +337,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
// only wide pointer boxes are handled as pointers
// thin pointer boxes with scalar allocators are handled by the general logic below
ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
- let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
+ let ptr_ty = Ty::new_mut_ptr(cx.tcx, self.ty.boxed_ty());
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
}
// `dyn* Trait` has the same ABI as `*mut dyn Trait`
ty::Dynamic(bounds, region, ty::DynStar) => {
- let ptr_ty = cx.tcx.mk_mut_ptr(cx.tcx.mk_dynamic(bounds, region, ty::Dyn));
+ let ptr_ty =
+ Ty::new_mut_ptr(cx.tcx, Ty::new_dynamic(cx.tcx, bounds, region, ty::Dyn));
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
}
_ => {}
@@ -415,4 +417,39 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
cx.pointee_infos.borrow_mut().insert((self.ty, offset), result);
result
}
+
+ fn scalar_copy_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Option<&'a Type> {
+ debug_assert!(self.is_sized());
+
+ // FIXME: this is a fairly arbitrary choice, but 128 bits on WASM
+ // (matching the 128-bit SIMD types proposal) and 256 bits on x64
+ // (like AVX2 registers) seems at least like a tolerable starting point.
+ let threshold = cx.data_layout().pointer_size * 4;
+ if self.layout.size() > threshold {
+ return None;
+ }
+
+ // Vectors, even for non-power-of-two sizes, have the same layout as
+ // arrays but don't count as aggregate types
+ // While LLVM theoretically supports non-power-of-two sizes, and they
+ // often work fine, sometimes x86-isel deals with them horribly
+ // (see #115212) so for now only use power-of-two ones.
+ if let FieldsShape::Array { count, .. } = self.layout.fields()
+ && count.is_power_of_two()
+ && let element = self.field(cx, 0)
+ && element.ty.is_integral()
+ {
+ // `cx.type_ix(bits)` is tempting here, but while that works great
+ // for things that *stay* as memory-to-memory copies, it also ends
+ // up suppressing vectorization as it introduces shifts when it
+ // extracts all the individual values.
+
+ let ety = element.llvm_type(cx);
+ return Some(cx.type_vector(ety, *count));
+ }
+
+ // FIXME: The above only handled integer arrays; surely more things
+ // would also be possible. Be careful about provenance, though!
+ None
+ }
}
diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs
index b19398e68..8800caa71 100644
--- a/compiler/rustc_codegen_llvm/src/va_arg.rs
+++ b/compiler/rustc_codegen_llvm/src/va_arg.rs
@@ -73,7 +73,7 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
let layout = bx.cx.layout_of(target_ty);
let (llty, size, align) = if indirect {
(
- bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).llvm_type(bx.cx),
+ bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx),
bx.cx.data_layout().pointer_size,
bx.cx.data_layout().pointer_align,
)
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 0ac12d32b..984efa210 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -48,7 +48,7 @@ libc = "0.2.50"
[dependencies.object]
version = "0.31.1"
default-features = false
-features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"]
+features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]
[target.'cfg(windows)'.dependencies.windows]
version = "0.48.0"
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 9aa2b2e2b..f73080182 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -9,6 +9,8 @@ codegen_ssa_archive_build_failure =
codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering
+codegen_ssa_binary_output_to_tty = option `-o` or `--emit` is used to write binary output type `{$shorthand}` to stdout, but stdout is a tty
+
codegen_ssa_check_installed_visual_studio = please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option.
codegen_ssa_copy_path = could not copy {$from} to {$to}: {$error}
@@ -19,6 +21,8 @@ codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
codegen_ssa_erroneous_constant = erroneous constant encountered
+codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
+
codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 8a00c42a0..b603a8787 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -5,14 +5,14 @@ use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, Handler};
-use rustc_fs_util::fix_windows_verbatim_for_gcc;
+use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
-use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME};
+use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME};
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
-use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
+use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip};
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
use rustc_session::cstore::DllImport;
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
@@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind;
use rustc_session::{filesearch, Session};
use rustc_span::symbol::Symbol;
use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
-use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy};
+use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy};
use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo};
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
@@ -68,6 +68,7 @@ pub fn link_binary<'a>(
) -> Result<(), ErrorGuaranteed> {
let _timer = sess.timer("link_binary");
let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata);
+ let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new();
for &crate_type in sess.crate_types().iter() {
// Ignore executable crates if we have -Z no-codegen, as they will error.
if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen())
@@ -97,12 +98,15 @@ pub fn link_binary<'a>(
.tempdir()
.unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error }));
let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps);
- let out_filename = out_filename(
+ let output = out_filename(
sess,
crate_type,
outputs,
codegen_results.crate_info.local_crate_name,
);
+ let crate_name = format!("{}", codegen_results.crate_info.local_crate_name);
+ let out_filename =
+ output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str()));
match crate_type {
CrateType::Rlib => {
let _timer = sess.timer("link_rlib");
@@ -152,6 +156,17 @@ pub fn link_binary<'a>(
);
}
}
+
+ if output.is_stdout() {
+ if output.is_tty() {
+ sess.emit_err(errors::BinaryOutputToTty {
+ shorthand: OutputType::Exe.shorthand(),
+ });
+ } else if let Err(e) = copy_to_stdout(&out_filename) {
+ sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e));
+ }
+ tempfiles_for_stdout_output.push(out_filename);
+ }
}
}
@@ -189,6 +204,11 @@ pub fn link_binary<'a>(
remove_temps_from_module(allocator_module);
}
+ // Remove the temporary files if output goes to stdout
+ for temp in tempfiles_for_stdout_output {
+ ensure_removed(sess.diagnostic(), &temp);
+ }
+
// If no requested outputs require linking, then the object temporaries should
// be kept.
if !sess.opts.output_types.should_link() {
@@ -893,7 +913,7 @@ fn link_natively<'a>(
linker_path: &linker_path,
exit_status: prog.status,
command: &cmd,
- escaped_output: &escaped_output,
+ escaped_output,
};
sess.diagnostic().emit_err(err);
// If MSVC's `link.exe` was expected but the return code
@@ -1188,6 +1208,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
if sanitizer.contains(SanitizerSet::HWADDRESS) {
link_sanitizer_runtime(sess, linker, "hwasan");
}
+ if sanitizer.contains(SanitizerSet::SAFESTACK) {
+ link_sanitizer_runtime(sess, linker, "safestack");
+ }
}
fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
@@ -1299,44 +1322,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
sess.emit_fatal(errors::LinkerFileStem);
});
-
- // Remove any version postfix.
- let stem = stem
- .rsplit_once('-')
- .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
- .unwrap_or(stem);
-
- // GCC/Clang can have an optional target prefix.
- let flavor = if stem == "emcc" {
- LinkerFlavor::EmCc
- } else if stem == "gcc"
- || stem.ends_with("-gcc")
- || stem == "g++"
- || stem.ends_with("-g++")
- || stem == "clang"
- || stem.ends_with("-clang")
- || stem == "clang++"
- || stem.ends_with("-clang++")
- {
- LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target)
- } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") {
- LinkerFlavor::WasmLld(Cc::No)
- } else if stem == "ld" || stem.ends_with("-ld") {
- LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target)
- } else if stem == "ld.lld" {
- LinkerFlavor::Gnu(Cc::No, Lld::Yes)
- } else if stem == "link" {
- LinkerFlavor::Msvc(Lld::No)
- } else if stem == "lld-link" {
- LinkerFlavor::Msvc(Lld::Yes)
- } else if stem == "lld" || stem == "rust-lld" {
- let lld_flavor = sess.target.linker_flavor.lld_flavor();
- LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target)
- } else {
- // fall back to the value in the target spec
- sess.target.linker_flavor
- };
-
+ let flavor = sess.target.linker_flavor.with_linker_hints(stem);
Some((linker, flavor))
}
(None, None) => None,
@@ -1346,7 +1332,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
// linker and linker flavor specified via command line have precedence over what the target
// specification specifies
let linker_flavor =
- sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target));
+ sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor));
if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) {
return ret;
}
@@ -1702,7 +1688,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool {
/// instead of being found somewhere on the host system.
/// We only provide such support for a very limited number of targets.
fn self_contained(sess: &Session, crate_type: CrateType) -> bool {
- if let Some(self_contained) = sess.opts.cg.link_self_contained {
+ if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set {
if sess.target.link_self_contained == LinkSelfContainedDefault::False {
sess.emit_err(errors::UnsupportedLinkSelfContained);
}
@@ -2131,7 +2117,14 @@ fn linker_with_args<'a>(
cmd.add_as_needed();
// Local native libraries of all kinds.
- add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
+ add_local_native_libraries(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ link_output_kind,
+ );
// Upstream rust crates and their non-dynamic native libraries.
add_upstream_rust_crates(
@@ -2141,10 +2134,18 @@ fn linker_with_args<'a>(
codegen_results,
crate_type,
tmpdir,
+ link_output_kind,
);
// Dynamic native libraries from upstream crates.
- add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir);
+ add_upstream_native_libraries(
+ cmd,
+ sess,
+ archive_builder_builder,
+ codegen_results,
+ tmpdir,
+ link_output_kind,
+ );
// Link with the import library generated for any raw-dylib functions.
for (raw_dylib_name, raw_dylib_imports) in
@@ -2245,7 +2246,8 @@ fn add_order_independent_options(
out_filename: &Path,
tmpdir: &Path,
) {
- add_gcc_ld_path(cmd, sess, flavor);
+ // Take care of the flavors and CLI options requesting the `lld` linker.
+ add_lld_args(cmd, sess, flavor);
add_apple_sdk(cmd, sess, flavor);
@@ -2290,11 +2292,13 @@ fn add_order_independent_options(
} else if flavor == LinkerFlavor::Bpf {
cmd.arg("--cpu");
cmd.arg(&codegen_results.crate_info.target_cpu);
- cmd.arg("--cpu-features");
- cmd.arg(match &sess.opts.cg.target_feature {
- feat if !feat.is_empty() => feat.as_ref(),
- _ => sess.target.options.features.as_ref(),
- });
+ if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features]
+ .into_iter()
+ .find(|feat| !feat.is_empty())
+ {
+ cmd.arg("--cpu-features");
+ cmd.arg(feat);
+ }
}
cmd.linker_plugin_lto();
@@ -2399,6 +2403,7 @@ fn add_native_libs_from_crate(
cnum: CrateNum,
link_static: bool,
link_dynamic: bool,
+ link_output_kind: LinkOutputKind,
) {
if !sess.opts.unstable_opts.link_native_libraries {
// If `-Zlink-native-libraries=false` is set, then the assumption is that an
@@ -2478,8 +2483,16 @@ fn add_native_libs_from_crate(
}
}
NativeLibKind::Unspecified => {
- if link_dynamic {
- cmd.link_dylib(name, verbatim, true);
+ // If we are generating a static binary, prefer static library when the
+ // link kind is unspecified.
+ if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs {
+ if link_static {
+ cmd.link_staticlib(name, verbatim)
+ }
+ } else {
+ if link_dynamic {
+ cmd.link_dylib(name, verbatim, true);
+ }
}
}
NativeLibKind::Framework { as_needed } => {
@@ -2506,6 +2519,7 @@ fn add_local_native_libraries(
archive_builder_builder: &dyn ArchiveBuilderBuilder,
codegen_results: &CodegenResults,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
if sess.opts.unstable_opts.link_native_libraries {
// User-supplied library search paths (-L on the command line). These are the same paths
@@ -2535,6 +2549,7 @@ fn add_local_native_libraries(
LOCAL_CRATE,
link_static,
link_dynamic,
+ link_output_kind,
);
}
@@ -2545,6 +2560,7 @@ fn add_upstream_rust_crates<'a>(
codegen_results: &CodegenResults,
crate_type: CrateType,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
// All of the heavy lifting has previously been accomplished by the
// dependency_format module of the compiler. This is just crawling the
@@ -2622,6 +2638,7 @@ fn add_upstream_rust_crates<'a>(
cnum,
link_static,
link_dynamic,
+ link_output_kind,
);
}
}
@@ -2632,6 +2649,7 @@ fn add_upstream_native_libraries(
archive_builder_builder: &dyn ArchiveBuilderBuilder,
codegen_results: &CodegenResults,
tmpdir: &Path,
+ link_output_kind: LinkOutputKind,
) {
let search_path = OnceCell::new();
for &cnum in &codegen_results.crate_info.used_crates {
@@ -2660,10 +2678,35 @@ fn add_upstream_native_libraries(
cnum,
link_static,
link_dynamic,
+ link_output_kind,
);
}
}
+// Rehome lib paths (which exclude the library file name) that point into the sysroot lib directory
+// to be relative to the sysroot directory, which may be a relative path specified by the user.
+//
+// If the sysroot is a relative path, and the sysroot libs are specified as an absolute path, the
+// linker command line can be non-deterministic due to the paths including the current working
+// directory. The linker command line needs to be deterministic since it appears inside the PDB
+// file generated by the MSVC linker. See https://github.com/rust-lang/rust/issues/112586.
+//
+// The returned path will always have `fix_windows_verbatim_for_gcc()` applied to it.
+fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf {
+ let sysroot_lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
+ let canonical_sysroot_lib_path =
+ { try_canonicalize(&sysroot_lib_path).unwrap_or_else(|_| sysroot_lib_path.clone()) };
+
+ let canonical_lib_dir = try_canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf());
+ if canonical_lib_dir == canonical_sysroot_lib_path {
+ // This path, returned by `target_filesearch().get_lib_path()`, has
+ // already had `fix_windows_verbatim_for_gcc()` applied if needed.
+ sysroot_lib_path
+ } else {
+ fix_windows_verbatim_for_gcc(&lib_dir)
+ }
+}
+
// Adds the static "rlib" versions of all crates to the command line.
// There's a bit of magic which happens here specifically related to LTO,
// namely that we remove upstream object files.
@@ -2695,7 +2738,13 @@ fn add_static_crate<'a>(
let cratepath = &src.rlib.as_ref().unwrap().0;
let mut link_upstream = |path: &Path| {
- cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
+ let rlib_path = if let Some(dir) = path.parent() {
+ let file_name = path.file_name().expect("rlib path has no file name path component");
+ rehome_sysroot_lib_dir(sess, &dir).join(file_name)
+ } else {
+ fix_windows_verbatim_for_gcc(path)
+ };
+ cmd.link_rlib(&rlib_path);
};
if !are_upstream_rust_objects_already_included(sess)
@@ -2764,7 +2813,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
// what its name is
let parent = cratepath.parent();
if let Some(dir) = parent {
- cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
+ cmd.include_path(&rehome_sysroot_lib_dir(sess, dir));
}
let stem = cratepath.file_stem().unwrap().to_str().unwrap();
// Convert library file-stem into a cc -l argument.
@@ -2900,55 +2949,81 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro
}
}
-fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
- if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld {
- if let LinkerFlavor::Gnu(Cc::Yes, _)
- | LinkerFlavor::Darwin(Cc::Yes, _)
- | LinkerFlavor::WasmLld(Cc::Yes) = flavor
- {
- match ld_impl {
- LdImpl::Lld => {
- // Implement the "self-contained" part of -Zgcc-ld
- // by adding rustc distribution directories to the tool search path.
- for path in sess.get_tools_search_paths(false) {
- cmd.arg({
- let mut arg = OsString::from("-B");
- arg.push(path.join("gcc-ld"));
- arg
- });
- }
- // Implement the "linker flavor" part of -Zgcc-ld
- // by asking cc to use some kind of lld.
- cmd.arg("-fuse-ld=lld");
-
- if !flavor.is_gnu() {
- // Tell clang to use a non-default LLD flavor.
- // Gcc doesn't understand the target option, but we currently assume
- // that gcc is not used for Apple and Wasm targets (#97402).
- //
- // Note that we don't want to do that by default on macOS: e.g. passing a
- // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
- // shown in issue #101653 and the discussion in PR #101792.
- //
- // It could be required in some cases of cross-compiling with
- // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
- // which specific versions of clang, macOS SDK, host and target OS
- // combinations impact us here.
- //
- // So we do a simple first-approximation until we know more of what the
- // Apple targets require (and which would be handled prior to hitting this
- // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
- // this should be manually passed if needed. We specify the target when
- // targeting a different linker flavor on macOS, and that's also always
- // the case when targeting WASM.
- if sess.target.linker_flavor != sess.host.linker_flavor {
- cmd.arg(format!("--target={}", sess.target.llvm_target));
- }
- }
- }
- }
- } else {
- sess.emit_fatal(errors::OptionGccOnly);
+/// When using the linker flavors opting in to `lld`, or the unstable `-Zgcc-ld=lld` flag, add the
+/// necessary paths and arguments to invoke it:
+/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc,
+/// - or any `lld` available to `cc`.
+fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
+ let unstable_use_lld = sess.opts.unstable_opts.gcc_ld.is_some();
+ debug!("add_lld_args requested, flavor: '{flavor:?}', `-Zgcc-ld=lld`: {unstable_use_lld}");
+
+ // Sanity check: using the old unstable `-Zgcc-ld=lld` option requires a `cc`-using flavor.
+ let flavor_uses_cc = flavor.uses_cc();
+ if unstable_use_lld && !flavor_uses_cc {
+ sess.emit_fatal(errors::OptionGccOnly);
+ }
+
+ // If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`,
+ // we don't need to do anything.
+ let use_lld = flavor.uses_lld() || unstable_use_lld;
+ if !flavor_uses_cc || !use_lld {
+ return;
+ }
+
+ let self_contained_linker = sess.opts.cg.link_self_contained.linker();
+
+ // FIXME: some targets default to using `lld`, but users can only override the linker on the CLI
+ // and cannot yet select the precise linker flavor to opt out of that. See for example issue
+ // #113597 for the `thumbv6m-none-eabi` target: a driver is used, and its default linker
+ // conflicts with the target's flavor, causing unexpected arguments being passed.
+ //
+ // Until the new `LinkerFlavor`-like CLI options are stabilized, we only adopt MCP510's behavior
+ // if its dedicated unstable CLI flags are used, to keep the current sub-optimal stable
+ // behavior.
+ let using_mcp510 =
+ self_contained_linker || sess.opts.cg.linker_flavor.is_some_and(|f| f.is_unstable());
+ if !using_mcp510 && !unstable_use_lld {
+ return;
+ }
+
+ // 1. Implement the "self-contained" part of this feature by adding rustc distribution
+ // directories to the tool's search path.
+ if self_contained_linker || unstable_use_lld {
+ for path in sess.get_tools_search_paths(false) {
+ cmd.arg({
+ let mut arg = OsString::from("-B");
+ arg.push(path.join("gcc-ld"));
+ arg
+ });
+ }
+ }
+
+ // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of
+ // `lld` as the linker.
+ cmd.arg("-fuse-ld=lld");
+
+ if !flavor.is_gnu() {
+ // Tell clang to use a non-default LLD flavor.
+ // Gcc doesn't understand the target option, but we currently assume
+ // that gcc is not used for Apple and Wasm targets (#97402).
+ //
+ // Note that we don't want to do that by default on macOS: e.g. passing a
+ // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as
+ // shown in issue #101653 and the discussion in PR #101792.
+ //
+ // It could be required in some cases of cross-compiling with
+ // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know
+ // which specific versions of clang, macOS SDK, host and target OS
+ // combinations impact us here.
+ //
+ // So we do a simple first-approximation until we know more of what the
+ // Apple targets require (and which would be handled prior to hitting this
+ // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then
+ // this should be manually passed if needed. We specify the target when
+ // targeting a different linker flavor on macOS, and that's also always
+ // the case when targeting WASM.
+ if sess.target.linker_flavor != sess.host.linker_flavor {
+ cmd.arg(format!("--target={}", sess.target.llvm_target));
}
}
}
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index cd56f85cc..8ac86fa4b 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -13,6 +13,7 @@ use std::{env, mem, str};
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_metadata::find_native_static_library;
use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols;
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
@@ -659,8 +660,6 @@ impl<'a> Linker for GccLinker<'a> {
return;
}
- // FIXME(#99978) hide #[no_mangle] symbols for proc-macros
-
let is_windows = self.sess.target.is_like_windows;
let path = tmpdir.join(if is_windows { "list.def" } else { "list" });
@@ -1679,8 +1678,15 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
return exports.iter().map(ToString::to_string).collect();
}
- let mut symbols = Vec::new();
+ if let CrateType::ProcMacro = crate_type {
+ exported_symbols_for_proc_macro_crate(tcx)
+ } else {
+ exported_symbols_for_non_proc_macro(tcx, crate_type)
+ }
+}
+fn exported_symbols_for_non_proc_macro(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
+ let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
if info.level.is_below_threshold(export_threshold) {
@@ -1691,6 +1697,19 @@ pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<St
symbols
}
+fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec<String> {
+ // `exported_symbols` will be empty when !should_codegen.
+ if !tcx.sess.opts.output_types.should_codegen() {
+ return Vec::new();
+ }
+
+ let stable_crate_id = tcx.sess.local_stable_crate_id();
+ let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
+ let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx);
+
+ vec![proc_macro_decls_name, metadata_symbol_name]
+}
+
pub(crate) fn linked_symbols(
tcx: TyCtxt<'_>,
crate_type: CrateType,
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index ad27b854d..00e6acb5c 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -6,8 +6,8 @@ use std::path::Path;
use object::write::{self, StandardSegment, Symbol, SymbolSection};
use object::{
- elf, pe, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
- SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
+ elf, pe, xcoff, Architecture, BinaryFormat, Endianness, FileFlags, Object, ObjectSection,
+ ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
};
use snap::write::FrameEncoder;
@@ -20,7 +20,7 @@ use rustc_metadata::EncodedMetadata;
use rustc_session::cstore::MetadataLoader;
use rustc_session::Session;
use rustc_target::abi::Endian;
-use rustc_target::spec::{RelocModel, Target};
+use rustc_target::spec::{ef_avr_arch, RelocModel, Target};
/// The default metadata loader. This is used by cg_llvm and cg_clif.
///
@@ -35,6 +35,8 @@ use rustc_target::spec::{RelocModel, Target};
#[derive(Debug)]
pub struct DefaultMetadataLoader;
+static AIX_METADATA_SYMBOL_NAME: &'static str = "__aix_rust_metadata";
+
fn load_metadata_with(
path: &Path,
f: impl for<'a> FnOnce(&'a [u8]) -> Result<&'a [u8], String>,
@@ -48,7 +50,7 @@ fn load_metadata_with(
}
impl MetadataLoader for DefaultMetadataLoader {
- fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
+ fn get_rlib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
load_metadata_with(path, |data| {
let archive = object::read::archive::ArchiveFile::parse(&*data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
@@ -60,7 +62,11 @@ impl MetadataLoader for DefaultMetadataLoader {
let data = entry
.data(data)
.map_err(|e| format!("failed to parse rlib '{}': {}", path.display(), e))?;
- return search_for_section(path, data, ".rmeta");
+ if target.is_like_aix {
+ return get_metadata_xcoff(path, data);
+ } else {
+ return search_for_section(path, data, ".rmeta");
+ }
}
}
@@ -68,8 +74,12 @@ impl MetadataLoader for DefaultMetadataLoader {
})
}
- fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result<OwnedSlice, String> {
- load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
+ fn get_dylib_metadata(&self, target: &Target, path: &Path) -> Result<OwnedSlice, String> {
+ if target.is_like_aix {
+ load_metadata_with(path, |data| get_metadata_xcoff(path, data))
+ } else {
+ load_metadata_with(path, |data| search_for_section(path, data, ".rustc"))
+ }
}
}
@@ -141,6 +151,33 @@ fn add_gnu_property_note(
file.append_section_data(section, &data, 8);
}
+pub(super) fn get_metadata_xcoff<'a>(path: &Path, data: &'a [u8]) -> Result<&'a [u8], String> {
+ let Ok(file) = object::File::parse(data) else {
+ return Ok(data);
+ };
+ let info_data = search_for_section(path, data, ".info")?;
+ if let Some(metadata_symbol) =
+ file.symbols().find(|sym| sym.name() == Ok(AIX_METADATA_SYMBOL_NAME))
+ {
+ let offset = metadata_symbol.address() as usize;
+ if offset < 4 {
+ return Err(format!("Invalid metadata symbol offset: {}", offset));
+ }
+ // The offset specifies the location of rustc metadata in the comment section.
+ // The metadata is preceded by a 4-byte length field.
+ let len = u32::from_be_bytes(info_data[(offset - 4)..offset].try_into().unwrap()) as usize;
+ if offset + len > (info_data.len() as usize) {
+ return Err(format!(
+ "Metadata at offset {} with size {} is beyond .info section",
+ offset, len
+ ));
+ }
+ return Ok(&info_data[offset..(offset + len)]);
+ } else {
+ return Err(format!("Unable to find symbol {}", AIX_METADATA_SYMBOL_NAME));
+ };
+}
+
pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
let endianness = match sess.target.options.endian {
Endian::Little => Endianness::Little,
@@ -183,6 +220,8 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
BinaryFormat::MachO
} else if sess.target.is_like_windows {
BinaryFormat::Coff
+ } else if sess.target.is_like_aix {
+ BinaryFormat::Xcoff
} else {
BinaryFormat::Elf
};
@@ -245,8 +284,24 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static
e_flags
}
Architecture::LoongArch64 => {
- // Source: https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html#_e_flags_identifies_abi_type_and_version
- elf::EF_LARCH_OBJABI_V1 | elf::EF_LARCH_ABI_DOUBLE_FLOAT
+ // Source: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc#e_flags-identifies-abi-type-and-version
+ let mut e_flags: u32 = elf::EF_LARCH_OBJABI_V1;
+ let features = &sess.target.options.features;
+
+ // Select the appropriate floating-point ABI
+ if features.contains("+d") {
+ e_flags |= elf::EF_LARCH_ABI_DOUBLE_FLOAT;
+ } else if features.contains("+f") {
+ e_flags |= elf::EF_LARCH_ABI_SINGLE_FLOAT;
+ } else {
+ e_flags |= elf::EF_LARCH_ABI_SOFT_FLOAT;
+ }
+ e_flags
+ }
+ Architecture::Avr => {
+ // Resolve the ISA revision and set
+ // the appropriate EF_AVR_ARCH flag.
+ ef_avr_arch(&sess.target.options.cpu)
}
_ => 0,
};
@@ -351,11 +406,15 @@ pub fn create_wrapper_file(
// to add a case above.
return (data.to_vec(), MetadataPosition::Last);
};
- let section = file.add_section(
- file.segment_name(StandardSegment::Debug).to_vec(),
- section_name,
- SectionKind::Debug,
- );
+ let section = if file.format() == BinaryFormat::Xcoff {
+ file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug)
+ } else {
+ file.add_section(
+ file.segment_name(StandardSegment::Debug).to_vec(),
+ section_name,
+ SectionKind::Debug,
+ )
+ };
match file.format() {
BinaryFormat::Coff => {
file.section_mut(section).flags =
@@ -365,6 +424,31 @@ pub fn create_wrapper_file(
file.section_mut(section).flags =
SectionFlags::Elf { sh_flags: elf::SHF_EXCLUDE as u64 };
}
+ BinaryFormat::Xcoff => {
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
+ file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
+ file.section_mut(section).flags =
+ SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
+
+ let len = data.len() as u32;
+ let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
+ // Add a symbol referring to the data in .info section.
+ file.add_symbol(Symbol {
+ name: AIX_METADATA_SYMBOL_NAME.into(),
+ value: offset + 4,
+ size: 0,
+ kind: SymbolKind::Unknown,
+ scope: SymbolScope::Compilation,
+ weak: false,
+ section: SymbolSection::Section(section),
+ flags: SymbolFlags::Xcoff {
+ n_sclass: xcoff::C_INFO,
+ x_smtyp: xcoff::C_HIDEXT,
+ x_smclas: xcoff::C_HIDEXT,
+ containing_csect: None,
+ },
+ });
+ }
_ => {}
};
file.append_section_data(section, data, 1);
@@ -401,6 +485,9 @@ pub fn create_compressed_metadata_file(
let Some(mut file) = create_object_file(sess) else {
return compressed.to_vec();
};
+ if file.format() == BinaryFormat::Xcoff {
+ return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
+ }
let section = file.add_section(
file.segment_name(StandardSegment::Data).to_vec(),
b".rustc".to_vec(),
@@ -430,3 +517,61 @@ pub fn create_compressed_metadata_file(
file.write().unwrap()
}
+
+/// * Xcoff - On AIX, custom sections are merged into predefined sections,
+/// so custom .rustc section is not preserved during linking.
+/// For this reason, we store metadata in predefined .info section, and
+/// define a symbol to reference the metadata. To preserve metadata during
+/// linking on AIX, we have to
+/// 1. Create an empty .text section, a empty .data section.
+/// 2. Define an empty symbol named `symbol_name` inside .data section.
+/// 3. Define an symbol named `AIX_METADATA_SYMBOL_NAME` referencing
+/// data inside .info section.
+/// From XCOFF's view, (2) creates a csect entry in the symbol table, the
+/// symbol created by (3) is a info symbol for the preceding csect. Thus
+/// two symbols are preserved during linking and we can use the second symbol
+/// to reference the metadata.
+pub fn create_compressed_metadata_file_for_xcoff(
+ mut file: write::Object<'_>,
+ data: &[u8],
+ symbol_name: &str,
+) -> Vec<u8> {
+ assert!(file.format() == BinaryFormat::Xcoff);
+ // AIX system linker may aborts if it meets a valid XCOFF file in archive with no .text, no .data and no .bss.
+ file.add_section(Vec::new(), b".text".to_vec(), SectionKind::Text);
+ let data_section = file.add_section(Vec::new(), b".data".to_vec(), SectionKind::Data);
+ let section = file.add_section(Vec::new(), b".info".to_vec(), SectionKind::Debug);
+ file.add_file_symbol("lib.rmeta".into());
+ file.section_mut(section).flags = SectionFlags::Xcoff { s_flags: xcoff::STYP_INFO as u32 };
+ // Add a global symbol to data_section.
+ file.add_symbol(Symbol {
+ name: symbol_name.as_bytes().into(),
+ value: 0,
+ size: 0,
+ kind: SymbolKind::Data,
+ scope: SymbolScope::Dynamic,
+ weak: true,
+ section: SymbolSection::Section(data_section),
+ flags: SymbolFlags::None,
+ });
+ let len = data.len() as u32;
+ let offset = file.append_section_data(section, &len.to_be_bytes(), 1);
+ // Add a symbol referring to the rustc metadata.
+ file.add_symbol(Symbol {
+ name: AIX_METADATA_SYMBOL_NAME.into(),
+ value: offset + 4, // The metadata is preceded by a 4-byte length field.
+ size: 0,
+ kind: SymbolKind::Unknown,
+ scope: SymbolScope::Dynamic,
+ weak: false,
+ section: SymbolSection::Section(section),
+ flags: SymbolFlags::Xcoff {
+ n_sclass: xcoff::C_INFO,
+ x_smtyp: xcoff::C_HIDEXT,
+ x_smclas: xcoff::C_HIDEXT,
+ containing_csect: None,
+ },
+ });
+ file.append_section_data(section, data, 1);
+ file.write().unwrap()
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index c323372bd..ececa29b2 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -9,11 +9,9 @@ use crate::{
};
use jobserver::{Acquired, Client};
use rustc_ast::attr;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::memmap::Mmap;
-use rustc_data_structures::profiling::SelfProfilerRef;
-use rustc_data_structures::profiling::TimingGuard;
-use rustc_data_structures::profiling::VerboseTimingGuard;
+use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard};
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::Emitter;
use rustc_errors::{translation::Translate, DiagnosticId, FatalError, Handler, Level};
@@ -23,12 +21,13 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_incremental::{
copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir, in_incr_comp_dir_sess,
};
+use rustc_metadata::fs::copy_to_stdout;
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::exported_symbols::SymbolExportInfo;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
-use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
+use rustc_session::config::{self, CrateType, Lto, OutFileName, OutputFilenames, OutputType};
use rustc_session::config::{Passes, SwitchWithOptPath};
use rustc_session::Session;
use rustc_span::source_map::SourceMap;
@@ -36,6 +35,7 @@ use rustc_span::symbol::sym;
use rustc_span::{BytePos, FileName, InnerSpan, Pos, Span};
use rustc_target::spec::{MergeFunctions, SanitizerSet};
+use crate::errors::ErrorCreatingRemarkDir;
use std::any::Any;
use std::borrow::Cow;
use std::fs;
@@ -321,7 +321,6 @@ pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo
#[derive(Clone)]
pub struct CodegenContext<B: WriteBackendMethods> {
// Resources needed when running LTO
- pub backend: B,
pub prof: SelfProfilerRef,
pub lto: Lto,
pub save_temps: bool,
@@ -339,18 +338,17 @@ pub struct CodegenContext<B: WriteBackendMethods> {
pub msvc_imps_needed: bool,
pub is_pe_coff: bool,
pub target_can_use_split_dwarf: bool,
- pub target_pointer_width: u32,
pub target_arch: String,
- pub debuginfo: config::DebugInfo,
pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
pub split_dwarf_kind: rustc_session::config::SplitDwarfKind,
- /// Number of cgus excluding the allocator/metadata modules
- pub total_cgus: usize,
/// Handler to use for diagnostics produced during codegen.
pub diag_emitter: SharedEmitter,
/// LLVM optimizations for which we want to print remarks.
pub remark: Passes,
+ /// Directory into which should the LLVM optimization remarks be written.
+ /// If `None`, they will be written to stderr.
+ pub remark_dir: Option<PathBuf>,
/// Worker thread number
pub worker: usize,
/// The incremental compilation session directory, or None if we are not
@@ -442,7 +440,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
target_cpu: String,
metadata: EncodedMetadata,
metadata_module: Option<CompiledModule>,
- total_cgus: usize,
) -> OngoingCodegen<B> {
let (coordinator_send, coordinator_receive) = channel();
let sess = tcx.sess;
@@ -470,7 +467,6 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
shared_emitter,
codegen_worker_send,
coordinator_receive,
- total_cgus,
sess.jobserver.clone(),
Arc::new(regular_config),
Arc::new(metadata_config),
@@ -498,8 +494,8 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
sess: &Session,
compiled_modules: &CompiledModules,
-) -> FxHashMap<WorkProductId, WorkProduct> {
- let mut work_products = FxHashMap::default();
+) -> FxIndexMap<WorkProductId, WorkProduct> {
+ let mut work_products = FxIndexMap::default();
if sess.opts.incremental.is_none() {
return work_products;
@@ -535,9 +531,16 @@ fn produce_final_output_artifacts(
let mut user_wants_objects = false;
// Produce final compile outputs.
- let copy_gracefully = |from: &Path, to: &Path| {
- if let Err(e) = fs::copy(from, to) {
- sess.emit_err(errors::CopyPath::new(from, to, e));
+ let copy_gracefully = |from: &Path, to: &OutFileName| match to {
+ OutFileName::Stdout => {
+ if let Err(e) = copy_to_stdout(from) {
+ sess.emit_err(errors::CopyPath::new(from, to.as_path(), e));
+ }
+ }
+ OutFileName::Real(path) => {
+ if let Err(e) = fs::copy(from, path) {
+ sess.emit_err(errors::CopyPath::new(from, path, e));
+ }
}
};
@@ -547,7 +550,12 @@ fn produce_final_output_artifacts(
// to copy `foo.0.x` to `foo.x`.
let module_name = Some(&compiled_modules.modules[0].name[..]);
let path = crate_output.temp_path(output_type, module_name);
- copy_gracefully(&path, &crate_output.path(output_type));
+ let output = crate_output.path(output_type);
+ if !output_type.is_text_output() && output.is_tty() {
+ sess.emit_err(errors::BinaryOutputToTty { shorthand: output_type.shorthand() });
+ } else {
+ copy_gracefully(&path, &output);
+ }
if !sess.opts.cg.save_temps && !keep_numbered {
// The user just wants `foo.x`, not `foo.#module-name#.x`.
ensure_removed(sess.diagnostic(), &path);
@@ -633,10 +641,10 @@ fn produce_final_output_artifacts(
// rlib.
let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe);
- let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1;
+ let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units().as_usize() > 1;
let keep_numbered_objects =
- needs_crate_object || (user_wants_objects && sess.codegen_units() > 1);
+ needs_crate_object || (user_wants_objects && sess.codegen_units().as_usize() > 1);
for module in compiled_modules.modules.iter() {
if let Some(ref path) = module.object {
@@ -674,7 +682,7 @@ fn produce_final_output_artifacts(
// These are used in linking steps and will be cleaned up afterward.
}
-pub enum WorkItem<B: WriteBackendMethods> {
+pub(crate) enum WorkItem<B: WriteBackendMethods> {
/// Optimize a newly codegened, totally unoptimized module.
Optimize(ModuleCodegen<B::Module>),
/// Copy the post-LTO artifacts from the incremental cache to the output
@@ -692,49 +700,57 @@ impl<B: WriteBackendMethods> WorkItem<B> {
}
}
- fn start_profiling<'a>(&self, cgcx: &'a CodegenContext<B>) -> TimingGuard<'a> {
- match *self {
- WorkItem::Optimize(ref m) => {
- cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name)
- }
- WorkItem::CopyPostLtoArtifacts(ref m) => cgcx
- .prof
- .generic_activity_with_arg("codegen_copy_artifacts_from_incr_cache", &*m.name),
- WorkItem::LTO(ref m) => {
- cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name())
- }
- }
- }
-
/// Generate a short description of this work item suitable for use as a thread name.
fn short_description(&self) -> String {
- // `pthread_setname()` on *nix is limited to 15 characters and longer names are ignored.
- // Use very short descriptions in this case to maximize the space available for the module name.
- // Windows does not have that limitation so use slightly more descriptive names there.
+ // `pthread_setname()` on *nix ignores anything beyond the first 15
+ // bytes. Use short descriptions to maximize the space available for
+ // the module name.
+ #[cfg(not(windows))]
+ fn desc(short: &str, _long: &str, name: &str) -> String {
+ // The short label is three bytes, and is followed by a space. That
+ // leaves 11 bytes for the CGU name. How we obtain those 11 bytes
+ // depends on the the CGU name form.
+ //
+ // - Non-incremental, e.g. `regex.f10ba03eb5ec7975-cgu.0`: the part
+ // before the `-cgu.0` is the same for every CGU, so use the
+ // `cgu.0` part. The number suffix will be different for each
+ // CGU.
+ //
+ // - Incremental (normal), e.g. `2i52vvl2hco29us0`: use the whole
+ // name because each CGU will have a unique ASCII hash, and the
+ // first 11 bytes will be enough to identify it.
+ //
+ // - Incremental (with `-Zhuman-readable-cgu-names`), e.g.
+ // `regex.f10ba03eb5ec7975-re_builder.volatile`: use the whole
+ // name. The first 11 bytes won't be enough to uniquely identify
+ // it, but no obvious substring will, and this is a rarely used
+ // option so it doesn't matter much.
+ //
+ assert_eq!(short.len(), 3);
+ let name = if let Some(index) = name.find("-cgu.") {
+ &name[index + 1..] // +1 skips the leading '-'.
+ } else {
+ name
+ };
+ format!("{short} {name}")
+ }
+
+ // Windows has no thread name length limit, so use more descriptive names.
+ #[cfg(windows)]
+ fn desc(_short: &str, long: &str, name: &str) -> String {
+ format!("{long} {name}")
+ }
+
match self {
- WorkItem::Optimize(m) => {
- #[cfg(windows)]
- return format!("optimize module {}", m.name);
- #[cfg(not(windows))]
- return format!("opt {}", m.name);
- }
- WorkItem::CopyPostLtoArtifacts(m) => {
- #[cfg(windows)]
- return format!("copy LTO artifacts for {}", m.name);
- #[cfg(not(windows))]
- return format!("copy {}", m.name);
- }
- WorkItem::LTO(m) => {
- #[cfg(windows)]
- return format!("LTO module {}", m.name());
- #[cfg(not(windows))]
- return format!("LTO {}", m.name());
- }
+ WorkItem::Optimize(m) => desc("opt", "optimize module {}", &m.name),
+ WorkItem::CopyPostLtoArtifacts(m) => desc("cpy", "copy LTO artifacts for {}", &m.name),
+ WorkItem::LTO(m) => desc("lto", "LTO module {}", m.name()),
}
}
}
-enum WorkItemResult<B: WriteBackendMethods> {
+/// A result produced by the backend.
+pub(crate) enum WorkItemResult<B: WriteBackendMethods> {
Compiled(CompiledModule),
NeedsLink(ModuleCodegen<B::Module>),
NeedsFatLTO(FatLTOInput<B>),
@@ -746,21 +762,6 @@ pub enum FatLTOInput<B: WriteBackendMethods> {
InMemory(ModuleCodegen<B::Module>),
}
-fn execute_work_item<B: ExtraBackendMethods>(
- cgcx: &CodegenContext<B>,
- work_item: WorkItem<B>,
-) -> Result<WorkItemResult<B>, FatalError> {
- let module_config = cgcx.config(work_item.module_kind());
-
- match work_item {
- WorkItem::Optimize(module) => execute_optimize_work_item(cgcx, module, module_config),
- WorkItem::CopyPostLtoArtifacts(module) => {
- Ok(execute_copy_from_cache_work_item(cgcx, module, module_config))
- }
- WorkItem::LTO(module) => execute_lto_work_item(cgcx, module, module_config),
- }
-}
-
/// Actual LTO type we end up choosing based on multiple factors.
pub enum ComputedLtoType {
No,
@@ -941,38 +942,41 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
}
}
-pub enum Message<B: WriteBackendMethods> {
+/// Messages sent to the coordinator.
+pub(crate) enum Message<B: WriteBackendMethods> {
+ /// A jobserver token has become available. Sent from the jobserver helper
+ /// thread.
Token(io::Result<Acquired>),
- NeedsFatLTO {
- result: FatLTOInput<B>,
- worker_id: usize,
- },
- NeedsThinLTO {
- name: String,
- thin_buffer: B::ThinBuffer,
- worker_id: usize,
- },
- NeedsLink {
- module: ModuleCodegen<B::Module>,
- worker_id: usize,
- },
- Done {
- result: Result<CompiledModule, Option<WorkerFatalError>>,
- worker_id: usize,
- },
- CodegenDone {
- llvm_work_item: WorkItem<B>,
- cost: u64,
- },
+
+ /// The backend has finished processing a work item for a codegen unit.
+ /// Sent from a backend worker thread.
+ WorkItem { result: Result<WorkItemResult<B>, Option<WorkerFatalError>>, worker_id: usize },
+
+ /// The frontend has finished generating something (backend IR or a
+ /// post-LTO artifact) for a codegen unit, and it should be passed to the
+ /// backend. Sent from the main thread.
+ CodegenDone { llvm_work_item: WorkItem<B>, cost: u64 },
+
+ /// Similar to `CodegenDone`, but for reusing a pre-LTO artifact
+ /// Sent from the main thread.
AddImportOnlyModule {
module_data: SerializedModule<B::ModuleBuffer>,
work_product: WorkProduct,
},
+
+ /// The frontend has finished generating everything for all codegen units.
+ /// Sent from the main thread.
CodegenComplete,
- CodegenItem,
+
+ /// Some normal-ish compiler error occurred, and codegen should be wound
+ /// down. Sent from the main thread.
CodegenAborted,
}
+/// A message sent from the coordinator thread to the main thread telling it to
+/// process another codegen unit.
+pub struct CguMessage;
+
type DiagnosticArgName<'source> = Cow<'source, str>;
struct Diagnostic {
@@ -994,9 +998,8 @@ fn start_executing_work<B: ExtraBackendMethods>(
tcx: TyCtxt<'_>,
crate_info: &CrateInfo,
shared_emitter: SharedEmitter,
- codegen_worker_send: Sender<Message<B>>,
+ codegen_worker_send: Sender<CguMessage>,
coordinator_receive: Receiver<Box<dyn Any + Send>>,
- total_cgus: usize,
jobserver: Client,
regular_config: Arc<ModuleConfig>,
metadata_config: Arc<ModuleConfig>,
@@ -1063,8 +1066,18 @@ fn start_executing_work<B: ExtraBackendMethods>(
tcx.backend_optimization_level(())
};
let backend_features = tcx.global_backend_features(());
+
+ let remark_dir = if let Some(ref dir) = sess.opts.unstable_opts.remark_dir {
+ let result = fs::create_dir_all(dir).and_then(|_| dir.canonicalize());
+ match result {
+ Ok(dir) => Some(dir),
+ Err(error) => sess.emit_fatal(ErrorCreatingRemarkDir { error }),
+ }
+ } else {
+ None
+ };
+
let cgcx = CodegenContext::<B> {
- backend: backend.clone(),
crate_types: sess.crate_types().to_vec(),
each_linked_rlib_for_lto,
lto: sess.lto(),
@@ -1075,6 +1088,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
prof: sess.prof.clone(),
exported_symbols,
remark: sess.opts.cg.remark.clone(),
+ remark_dir,
worker: 0,
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()),
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
@@ -1085,13 +1099,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
metadata_module_config: metadata_config,
allocator_module_config: allocator_config,
tm_factory: backend.target_machine_factory(tcx.sess, ol, backend_features),
- total_cgus,
msvc_imps_needed: msvc_imps_needed(tcx),
is_pe_coff: tcx.sess.target.is_like_windows,
target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
- target_pointer_width: tcx.sess.target.pointer_width,
target_arch: tcx.sess.target.arch.to_string(),
- debuginfo: tcx.sess.opts.debuginfo,
split_debuginfo: tcx.sess.split_debuginfo(),
split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind,
};
@@ -1253,10 +1264,19 @@ fn start_executing_work<B: ExtraBackendMethods>(
let mut needs_thin_lto = Vec::new();
let mut lto_import_only_modules = Vec::new();
let mut started_lto = false;
- let mut codegen_aborted = false;
- // This flag tracks whether all items have gone through codegens
- let mut codegen_done = false;
+ /// Possible state transitions:
+ /// - Ongoing -> Completed
+ /// - Ongoing -> Aborted
+ /// - Completed -> Aborted
+ #[derive(Debug, PartialEq)]
+ enum CodegenState {
+ Ongoing,
+ Completed,
+ Aborted,
+ }
+ use CodegenState::*;
+ let mut codegen_state = Ongoing;
// This is the queue of LLVM work items that still need processing.
let mut work_items = Vec::<(WorkItem<B>, u64)>::new();
@@ -1276,10 +1296,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
// wait for all existing work to finish, so many of the conditions here
// only apply if codegen hasn't been aborted as they represent pending
// work to be done.
- while !codegen_done
+ while codegen_state == Ongoing
|| running > 0
|| main_thread_worker_state == MainThreadWorkerState::LLVMing
- || (!codegen_aborted
+ || (codegen_state == Completed
&& !(work_items.is_empty()
&& needs_fat_lto.is_empty()
&& needs_thin_lto.is_empty()
@@ -1289,7 +1309,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
// While there are still CGUs to be codegened, the coordinator has
// to decide how to utilize the compiler processes implicit Token:
// For codegenning more CGU or for running them through LLVM.
- if !codegen_done {
+ if codegen_state == Ongoing {
if main_thread_worker_state == MainThreadWorkerState::Idle {
// Compute the number of workers that will be running once we've taken as many
// items from the work queue as we can, plus one for the main thread. It's not
@@ -1302,9 +1322,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
let anticipated_running = running + additional_running + 1;
if !queue_full_enough(work_items.len(), anticipated_running) {
- // The queue is not full enough, codegen more items:
- if codegen_worker_send.send(Message::CodegenItem).is_err() {
- panic!("Could not send Message::CodegenItem to main thread")
+ // The queue is not full enough, process more codegen units:
+ if codegen_worker_send.send(CguMessage).is_err() {
+ panic!("Could not send CguMessage to main thread")
}
main_thread_worker_state = MainThreadWorkerState::Codegenning;
} else {
@@ -1326,10 +1346,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
spawn_work(cgcx, item);
}
}
- } else if codegen_aborted {
- // don't queue up any more work if codegen was aborted, we're
- // just waiting for our existing children to finish
- } else {
+ } else if codegen_state == Completed {
// If we've finished everything related to normal codegen
// then it must be the case that we've got some LTO work to do.
// Perform the serial work here of figuring out what we're
@@ -1396,11 +1413,15 @@ fn start_executing_work<B: ExtraBackendMethods>(
// Already making good use of that token
}
}
+ } else {
+ // Don't queue up any more work if codegen was aborted, we're
+ // just waiting for our existing children to finish.
+ assert!(codegen_state == Aborted);
}
// Spin up what work we can, only doing this while we've got available
// parallelism slots and work left to spawn.
- while !codegen_aborted && !work_items.is_empty() && running < tokens.len() {
+ while codegen_state != Aborted && !work_items.is_empty() && running < tokens.len() {
let (item, _) = work_items.pop().unwrap();
maybe_start_llvm_timer(prof, cgcx.config(item.module_kind()), &mut llvm_start_time);
@@ -1452,8 +1473,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
Err(e) => {
let msg = &format!("failed to acquire jobserver token: {}", e);
shared_emitter.fatal(msg);
- codegen_done = true;
- codegen_aborted = true;
+ codegen_state = Aborted;
}
}
}
@@ -1481,7 +1501,9 @@ fn start_executing_work<B: ExtraBackendMethods>(
}
Message::CodegenComplete => {
- codegen_done = true;
+ if codegen_state != Aborted {
+ codegen_state = Completed;
+ }
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
main_thread_worker_state = MainThreadWorkerState::Idle;
}
@@ -1493,58 +1515,59 @@ fn start_executing_work<B: ExtraBackendMethods>(
// then conditions above will ensure no more work is spawned but
// we'll keep executing this loop until `running` hits 0.
Message::CodegenAborted => {
- codegen_done = true;
- codegen_aborted = true;
+ codegen_state = Aborted;
}
- Message::Done { result: Ok(compiled_module), worker_id } => {
+
+ Message::WorkItem { result, worker_id } => {
free_worker(worker_id);
- match compiled_module.kind {
- ModuleKind::Regular => {
- compiled_modules.push(compiled_module);
+
+ match result {
+ Ok(WorkItemResult::Compiled(compiled_module)) => {
+ match compiled_module.kind {
+ ModuleKind::Regular => {
+ compiled_modules.push(compiled_module);
+ }
+ ModuleKind::Allocator => {
+ assert!(compiled_allocator_module.is_none());
+ compiled_allocator_module = Some(compiled_module);
+ }
+ ModuleKind::Metadata => bug!("Should be handled separately"),
+ }
+ }
+ Ok(WorkItemResult::NeedsLink(module)) => {
+ needs_link.push(module);
+ }
+ Ok(WorkItemResult::NeedsFatLTO(fat_lto_input)) => {
+ assert!(!started_lto);
+ needs_fat_lto.push(fat_lto_input);
+ }
+ Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer)) => {
+ assert!(!started_lto);
+ needs_thin_lto.push((name, thin_buffer));
+ }
+ Err(Some(WorkerFatalError)) => {
+ // Like `CodegenAborted`, wait for remaining work to finish.
+ codegen_state = Aborted;
}
- ModuleKind::Allocator => {
- assert!(compiled_allocator_module.is_none());
- compiled_allocator_module = Some(compiled_module);
+ Err(None) => {
+ // If the thread failed that means it panicked, so
+ // we abort immediately.
+ bug!("worker thread panicked");
}
- ModuleKind::Metadata => bug!("Should be handled separately"),
}
}
- Message::NeedsLink { module, worker_id } => {
- free_worker(worker_id);
- needs_link.push(module);
- }
- Message::NeedsFatLTO { result, worker_id } => {
- assert!(!started_lto);
- free_worker(worker_id);
- needs_fat_lto.push(result);
- }
- Message::NeedsThinLTO { name, thin_buffer, worker_id } => {
- assert!(!started_lto);
- free_worker(worker_id);
- needs_thin_lto.push((name, thin_buffer));
- }
+
Message::AddImportOnlyModule { module_data, work_product } => {
assert!(!started_lto);
- assert!(!codegen_done);
+ assert_eq!(codegen_state, Ongoing);
assert_eq!(main_thread_worker_state, MainThreadWorkerState::Codegenning);
lto_import_only_modules.push((module_data, work_product));
main_thread_worker_state = MainThreadWorkerState::Idle;
}
- // If the thread failed that means it panicked, so we abort immediately.
- Message::Done { result: Err(None), worker_id: _ } => {
- bug!("worker thread panicked");
- }
- Message::Done { result: Err(Some(WorkerFatalError)), worker_id } => {
- // Similar to CodegenAborted, wait for remaining work to finish.
- free_worker(worker_id);
- codegen_done = true;
- codegen_aborted = true;
- }
- Message::CodegenItem => bug!("the coordinator should not receive codegen requests"),
}
}
- if codegen_aborted {
+ if codegen_state == Aborted {
return Err(());
}
@@ -1659,22 +1682,11 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
fn drop(&mut self) {
let worker_id = self.worker_id;
let msg = match self.result.take() {
- Some(Ok(WorkItemResult::Compiled(m))) => {
- Message::Done::<B> { result: Ok(m), worker_id }
- }
- Some(Ok(WorkItemResult::NeedsLink(m))) => {
- Message::NeedsLink::<B> { module: m, worker_id }
- }
- Some(Ok(WorkItemResult::NeedsFatLTO(m))) => {
- Message::NeedsFatLTO::<B> { result: m, worker_id }
- }
- Some(Ok(WorkItemResult::NeedsThinLTO(name, thin_buffer))) => {
- Message::NeedsThinLTO::<B> { name, thin_buffer, worker_id }
- }
+ Some(Ok(result)) => Message::WorkItem::<B> { result: Ok(result), worker_id },
Some(Err(FatalError)) => {
- Message::Done::<B> { result: Err(Some(WorkerFatalError)), worker_id }
+ Message::WorkItem::<B> { result: Err(Some(WorkerFatalError)), worker_id }
}
- None => Message::Done::<B> { result: Err(None), worker_id },
+ None => Message::WorkItem::<B> { result: Err(None), worker_id },
};
drop(self.coordinator_send.send(Box::new(msg)));
}
@@ -1693,8 +1705,27 @@ fn spawn_work<B: ExtraBackendMethods>(cgcx: CodegenContext<B>, work: WorkItem<B>
// as a diagnostic was already sent off to the main thread - just
// surface that there was an error in this worker.
bomb.result = {
- let _prof_timer = work.start_profiling(&cgcx);
- Some(execute_work_item(&cgcx, work))
+ let module_config = cgcx.config(work.module_kind());
+
+ Some(match work {
+ WorkItem::Optimize(m) => {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("codegen_module_optimize", &*m.name);
+ execute_optimize_work_item(&cgcx, m, module_config)
+ }
+ WorkItem::CopyPostLtoArtifacts(m) => {
+ let _timer = cgcx.prof.generic_activity_with_arg(
+ "codegen_copy_artifacts_from_incr_cache",
+ &*m.name,
+ );
+ Ok(execute_copy_from_cache_work_item(&cgcx, m, module_config))
+ }
+ WorkItem::LTO(m) => {
+ let _timer =
+ cgcx.prof.generic_activity_with_arg("codegen_module_perform_lto", m.name());
+ execute_lto_work_item(&cgcx, m, module_config)
+ }
+ })
};
})
.expect("failed to spawn thread");
@@ -1800,7 +1831,7 @@ impl SharedEmitterMain {
handler.emit_diagnostic(&mut d);
}
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
- let msg = msg.strip_prefix("error: ").unwrap_or(&msg);
+ let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
let mut err = match level {
Level::Error { lint: false } => sess.struct_err(msg).forget_guarantee(),
@@ -1878,14 +1909,14 @@ pub struct OngoingCodegen<B: ExtraBackendMethods> {
pub metadata: EncodedMetadata,
pub metadata_module: Option<CompiledModule>,
pub crate_info: CrateInfo,
- pub codegen_worker_receive: Receiver<Message<B>>,
+ pub codegen_worker_receive: Receiver<CguMessage>,
pub shared_emitter_main: SharedEmitterMain,
pub output_filenames: Arc<OutputFilenames>,
pub coordinator: Coordinator<B>,
}
impl<B: ExtraBackendMethods> OngoingCodegen<B> {
- pub fn join(self, sess: &Session) -> (CodegenResults, FxHashMap<WorkProductId, WorkProduct>) {
+ pub fn join(self, sess: &Session) -> (CodegenResults, FxIndexMap<WorkProductId, WorkProduct>) {
let _timer = sess.timer("finish_ongoing_codegen");
self.shared_emitter_main.check(sess, true);
@@ -1910,7 +1941,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
// FIXME: time_llvm_passes support - does this use a global context or
// something?
- if sess.codegen_units() == 1 && sess.opts.unstable_opts.time_llvm_passes {
+ if sess.codegen_units().as_usize() == 1 && sess.opts.unstable_opts.time_llvm_passes {
self.backend.print_pass_timings()
}
@@ -1952,10 +1983,9 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
pub fn wait_for_signal_to_codegen_item(&self) {
match self.codegen_worker_receive.recv() {
- Ok(Message::CodegenItem) => {
- // Nothing to do
+ Ok(CguMessage) => {
+ // Ok to proceed.
}
- Ok(_) => panic!("unexpected message"),
Err(_) => {
// One of the LLVM threads must have panicked, fall through so
// error handling can be reached.
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index ab7dd1ba8..9133601ec 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -199,7 +199,7 @@ fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
cx.scalar_pair_element_backend_type(
cx.layout_of(match kind {
// vtable is the second field of `*mut dyn Trait`
- ty::Dyn => cx.tcx().mk_mut_ptr(target),
+ ty::Dyn => Ty::new_mut_ptr(cx.tcx(), target),
// vtable is the second field of `dyn* Trait`
ty::DynStar => target,
}),
@@ -295,7 +295,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let (base, info) = match bx.load_operand(src).val {
OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
- OperandValue::Ref(..) => bug!(),
+ OperandValue::Ref(..) | OperandValue::ZeroSized => bug!(),
};
OperandValue::Pair(base, info).store(bx, dst);
}
@@ -357,6 +357,13 @@ pub fn cast_shift_expr_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
}
+// Returns `true` if this session's target will use native wasm
+// exceptions. This means that the VM does the unwinding for
+// us
+pub fn wants_wasm_eh(sess: &Session) -> bool {
+ sess.target.is_like_wasm && sess.target.os != "emscripten"
+}
+
/// Returns `true` if this session's target will use SEH-based unwinding.
///
/// This is only true for MSVC targets, and even then the 64-bit MSVC target
@@ -366,6 +373,13 @@ pub fn wants_msvc_seh(sess: &Session) -> bool {
sess.target.is_like_msvc
}
+/// Returns `true` if this session's target requires the new exception
+/// handling LLVM IR instructions (catchpad / cleanuppad / ... instead
+/// of landingpad)
+pub fn wants_new_eh_instructions(sess: &Session) -> bool {
+ wants_wasm_eh(sess) || wants_msvc_seh(sess)
+}
+
pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
dst: Bx::Value,
@@ -380,7 +394,19 @@ pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
return;
}
- bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags);
+ if flags == MemFlags::empty()
+ && let Some(bty) = bx.cx().scalar_copy_backend_type(layout)
+ {
+ // I look forward to only supporting opaque pointers
+ let pty = bx.type_ptr_to(bty);
+ let src = bx.pointercast(src, pty);
+ let dst = bx.pointercast(dst, pty);
+
+ let temp = bx.load(bty, src, src_align);
+ bx.store(temp, dst, dst_align);
+ } else {
+ bx.memcpy(dst, dst_align, src, src_align, bx.cx().const_usize(size), flags);
+ }
}
pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
@@ -568,7 +594,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
) -> OngoingCodegen<B> {
// Skip crate items and just output metadata in -Z no-codegen mode.
if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() {
- let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None, 1);
+ let ongoing_codegen = start_async_codegen(backend, tcx, target_cpu, metadata, None);
ongoing_codegen.codegen_finished(tcx);
@@ -619,14 +645,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
})
});
- let ongoing_codegen = start_async_codegen(
- backend.clone(),
- tcx,
- target_cpu,
- metadata,
- metadata_module,
- codegen_units.len(),
- );
+ let ongoing_codegen =
+ start_async_codegen(backend.clone(), tcx, target_cpu, metadata, metadata_module);
// Codegen an allocator shim, if necessary.
if let Some(kind) = allocator_kind_for_codegen(tcx) {
diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs
index e1abb73a5..5a6807599 100644
--- a/compiler/rustc_codegen_ssa/src/common.rs
+++ b/compiler/rustc_codegen_ssa/src/common.rs
@@ -132,7 +132,7 @@ pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// all shifts). For 32- and 64-bit types, this matches the semantics
// of Java. (See related discussion on #1877 and #10183.)
-pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
lhs: Bx::Value,
rhs: Bx::Value,
@@ -143,7 +143,7 @@ pub fn build_unchecked_lshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx.shl(lhs, rhs)
}
-pub fn build_unchecked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+pub fn build_masked_rshift<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
bx: &mut Bx,
lhs_t: Ty<'tcx>,
lhs: Bx::Value,
diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs
deleted file mode 100644
index 569fd3f1a..000000000
--- a/compiler/rustc_codegen_ssa/src/coverageinfo/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod ffi;
-pub mod map;
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 6297f9134..e91f7b86e 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -93,8 +93,7 @@ fn push_debuginfo_type_name<'tcx>(
Err(e) => {
// Computing the layout can still fail here, e.g. if the target architecture
// cannot represent the type. See https://github.com/rust-lang/rust/issues/94961.
- // FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable.
- tcx.sess.fatal(format!("{}", e));
+ tcx.sess.emit_fatal(e.into_diagnostic());
}
}
} else {
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index cf4893b82..056b4abd2 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -83,6 +83,12 @@ impl IntoDiagnosticArg for DebugArgPath<'_> {
}
#[derive(Diagnostic)]
+#[diag(codegen_ssa_binary_output_to_tty)]
+pub struct BinaryOutputToTty {
+ pub shorthand: &'static str,
+}
+
+#[derive(Diagnostic)]
#[diag(codegen_ssa_ignoring_emit_path)]
pub struct IgnoringEmitPath {
pub extension: String,
@@ -336,7 +342,7 @@ pub struct LinkingFailed<'a> {
pub linker_path: &'a PathBuf,
pub exit_status: ExitStatus,
pub command: &'a Command,
- pub escaped_output: &'a str,
+ pub escaped_output: String,
}
impl IntoDiagnostic<'_> for LinkingFailed<'_> {
@@ -345,11 +351,13 @@ impl IntoDiagnostic<'_> for LinkingFailed<'_> {
diag.set_arg("linker_path", format!("{}", self.linker_path.display()));
diag.set_arg("exit_status", format!("{}", self.exit_status));
- diag.note(format!("{:?}", self.command)).note(self.escaped_output);
+ let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
+
+ diag.note(format!("{:?}", self.command)).note(self.escaped_output.to_string());
// Trying to match an error from OS linkers
// which by now we have no way to translate.
- if self.escaped_output.contains("undefined reference to") {
+ if contains_undefined_ref {
diag.note(fluent::codegen_ssa_extern_funcs_not_found)
.note(fluent::codegen_ssa_specify_libraries_to_link)
.note(fluent::codegen_ssa_use_cargo_directive);
@@ -1015,3 +1023,9 @@ pub struct TargetFeatureSafeTrait {
#[label(codegen_ssa_label_def)]
pub def: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(codegen_ssa_error_creating_remark_dir)]
+pub struct ErrorCreatingRemarkDir {
+ pub error: std::io::Error,
+}
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 31854c7f4..be4c81638 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -4,6 +4,7 @@
#![feature(if_let_guard)]
#![feature(int_roundings)]
#![feature(let_chains)]
+#![feature(negative_impls)]
#![feature(never_type)]
#![feature(strict_provenance)]
#![feature(try_blocks)]
@@ -47,7 +48,6 @@ pub mod back;
pub mod base;
pub mod codegen_attrs;
pub mod common;
-pub mod coverageinfo;
pub mod debuginfo;
pub mod errors;
pub mod glue;
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 3f0b64b11..9d1b3ce82 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1,5 +1,5 @@
use super::operand::OperandRef;
-use super::operand::OperandValue::{Immediate, Pair, Ref};
+use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized};
use super::place::PlaceRef;
use super::{CachedLlbb, FunctionCx, LocalRef};
@@ -79,8 +79,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
lltarget = fx.landing_pad_for(target);
}
if is_cleanupret {
- // MSVC cross-funclet jump - need a trampoline
- debug_assert!(base::wants_msvc_seh(fx.cx.tcx().sess));
+ // Cross-funclet jump - need a trampoline
+ debug_assert!(base::wants_new_eh_instructions(fx.cx.tcx().sess));
debug!("llbb_with_cleanup: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", self.bb, target);
let trampoline_llbb = Bx::append_block(fx.cx, fx.llfn, name);
@@ -177,9 +177,16 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
mir::UnwindAction::Terminate => {
- if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
- // SEH will abort automatically if an exception tries to
+ if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
+ // MSVC SEH will abort automatically if an exception tries to
// propagate out from cleanup.
+
+ // FIXME(@mirkootter): For wasm, we currently do not support terminate during
+ // cleanup, because this requires a few more changes: The current code
+ // caches the `terminate_block` for each function; funclet based code - however -
+ // requires a different terminate_block for each funclet
+ // Until this is implemented, we just do not unwind inside cleanup blocks
+
None
} else {
Some(fx.terminate_block())
@@ -427,6 +434,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
assert_eq!(align, op.layout.align.abi, "return place is unaligned!");
llval
}
+ ZeroSized => bug!("ZST return value shouldn't be in PassMode::Cast"),
};
let ty = bx.cast_backend_type(cast_ty);
let addr = bx.pointercast(llslot, bx.type_ptr_to(ty));
@@ -615,7 +623,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
let required = self.codegen_operand(bx, required).immediate();
let found = self.codegen_operand(bx, found).immediate();
- // It's `fn panic_bounds_check(index: usize, len: usize)`,
+ // It's `fn panic_misaligned_pointer_dereference(required: usize, found: usize)`,
// and `#[track_caller]` adds an implicit third argument.
(LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
}
@@ -862,13 +870,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// promotes any complex rvalues to constants.
if i == 2 && intrinsic.as_str().starts_with("simd_shuffle") {
if let mir::Operand::Constant(constant) = arg {
- let c = self.eval_mir_constant(constant);
- let (llval, ty) = self.simd_shuffle_indices(
- &bx,
- constant.span,
- self.monomorphize(constant.ty()),
- c,
- );
+ let (llval, ty) = self.simd_shuffle_indices(&bx, constant);
return OperandRef {
val: Immediate(llval),
layout: bx.layout_of(ty),
@@ -1279,7 +1281,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
destination,
target,
unwind,
- from_hir_call: _,
+ call_source: _,
fn_span,
} => self.codegen_call_terminator(
helper,
@@ -1386,6 +1388,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
(llval, align, true)
}
}
+ ZeroSized => match arg.mode {
+ PassMode::Indirect { .. } => {
+ // Though `extern "Rust"` doesn't pass ZSTs, some ABIs pass
+ // a pointer for `repr(C)` structs even when empty, so get
+ // one from an `alloca` (which can be left uninitialized).
+ let scratch = PlaceRef::alloca(bx, arg.layout);
+ (scratch.llval, scratch.align, true)
+ }
+ _ => bug!("ZST {op:?} wasn't ignored, but was passed with abi {arg:?}"),
+ },
};
if by_ref && !arg.is_indirect() {
@@ -1493,9 +1505,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if let Some(slot) = self.personality_slot {
slot
} else {
- let layout = cx.layout_of(
- cx.tcx().mk_tup(&[cx.tcx().mk_mut_ptr(cx.tcx().types.u8), cx.tcx().types.i32]),
- );
+ let layout = cx.layout_of(Ty::new_tup(
+ cx.tcx(),
+ &[Ty::new_mut_ptr(cx.tcx(), cx.tcx().types.u8), cx.tcx().types.i32],
+ ));
let slot = PlaceRef::alloca(bx, layout);
self.personality_slot = Some(slot);
slot
@@ -1517,7 +1530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// FIXME(eddyb) rename this to `eh_pad_for_uncached`.
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
let llbb = self.llbb(bb);
- if base::wants_msvc_seh(self.cx.sess()) {
+ if base::wants_new_eh_instructions(self.cx.sess()) {
let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
let funclet = cleanup_bx.cleanup_pad(None, &[]);
@@ -1576,6 +1589,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// } catch (...) {
// bar();
// }
+ //
+ // which creates an IR snippet like
+ //
+ // cs_terminate:
+ // %cs = catchswitch within none [%cp_terminate] unwind to caller
+ // cp_terminate:
+ // %cp = catchpad within %cs [null, i32 64, null]
+ // ...
+
llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
@@ -1718,7 +1740,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
IndirectOperand(tmp, index) => {
let op = bx.load_operand(tmp);
tmp.storage_dead(bx);
- self.locals[index] = LocalRef::Operand(op);
+ self.overwrite_local(index, LocalRef::Operand(op));
self.debug_introduce_local(bx, index);
}
DirectOperand(index) => {
@@ -1733,7 +1755,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout)
};
- self.locals[index] = LocalRef::Operand(op);
+ self.overwrite_local(index, LocalRef::Operand(op));
self.debug_introduce_local(bx, index);
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index 14fe84a14..babcf9bee 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -5,7 +5,6 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::{self, Ty};
-use rustc_span::source_map::Span;
use rustc_target::abi::Abi;
use super::FunctionCx;
@@ -59,22 +58,54 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
}
+ /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition
+ /// that the given `constant` is an `ConstantKind::Unevaluated` and must be convertible to
+ /// a `ValTree`. If you want a more general version of this, talk to `wg-const-eval` on zulip.
+ pub fn eval_unevaluated_mir_constant_to_valtree(
+ &self,
+ constant: &mir::Constant<'tcx>,
+ ) -> Result<Option<ty::ValTree<'tcx>>, ErrorHandled> {
+ let uv = match self.monomorphize(constant.literal) {
+ mir::ConstantKind::Unevaluated(uv, _) => uv.shrink(),
+ mir::ConstantKind::Ty(c) => match c.kind() {
+ // A constant that came from a const generic but was then used as an argument to old-style
+ // simd_shuffle (passing as argument instead of as a generic param).
+ rustc_type_ir::ConstKind::Value(valtree) => return Ok(Some(valtree)),
+ other => span_bug!(constant.span, "{other:#?}"),
+ },
+ // We should never encounter `ConstantKind::Val` unless MIR opts (like const prop) evaluate
+ // a constant and write that value back into `Operand`s. This could happen, but is unlikely.
+ // Also: all users of `simd_shuffle` are on unstable and already need to take a lot of care
+ // around intrinsics. For an issue to happen here, it would require a macro expanding to a
+ // `simd_shuffle` call without wrapping the constant argument in a `const {}` block, but
+ // the user pass through arbitrary expressions.
+ // FIXME(oli-obk): replace the magic const generic argument of `simd_shuffle` with a real
+ // const generic.
+ other => span_bug!(constant.span, "{other:#?}"),
+ };
+ let uv = self.monomorphize(uv);
+ self.cx.tcx().const_eval_resolve_for_typeck(
+ ty::ParamEnv::reveal_all(),
+ uv,
+ Some(constant.span),
+ )
+ }
+
/// process constant containing SIMD shuffle indices
pub fn simd_shuffle_indices(
&mut self,
bx: &Bx,
- span: Span,
- ty: Ty<'tcx>,
- constant: Result<ConstValue<'tcx>, ErrorHandled>,
+ constant: &mir::Constant<'tcx>,
) -> (Bx::Value, Ty<'tcx>) {
- constant
+ let ty = self.monomorphize(constant.ty());
+ let val = self
+ .eval_unevaluated_mir_constant_to_valtree(constant)
+ .ok()
+ .flatten()
.map(|val| {
let field_ty = ty.builtin_index().unwrap();
- let c = mir::ConstantKind::from_value(val, ty);
- let values: Vec<_> = bx
- .tcx()
- .destructure_mir_constant(ty::ParamEnv::reveal_all(), c)
- .fields
+ let values: Vec<_> = val
+ .unwrap_branch()
.iter()
.map(|field| {
if let Some(prim) = field.try_to_scalar() {
@@ -88,15 +119,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
})
.collect();
- let llval = bx.const_struct(&values, false);
- (llval, c.ty())
+ bx.const_struct(&values, false)
})
- .unwrap_or_else(|_| {
- bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span });
+ .unwrap_or_else(|| {
+ bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span: constant.span });
// We've errored, so we don't have to produce working code.
- let ty = self.monomorphize(ty);
let llty = bx.backend_type(bx.layout_of(ty));
- (bx.const_undef(llty), ty)
- })
+ bx.const_undef(llty)
+ });
+ (val, ty)
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
index f1fe49528..ee7046596 100644
--- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs
@@ -1,13 +1,12 @@
use crate::traits::*;
-use rustc_middle::mir::coverage::*;
use rustc_middle::mir::Coverage;
use rustc_middle::mir::SourceScope;
use super::FunctionCx;
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
- pub fn codegen_coverage(&self, bx: &mut Bx, coverage: Coverage, scope: SourceScope) {
+ pub fn codegen_coverage(&self, bx: &mut Bx, coverage: &Coverage, scope: SourceScope) {
// Determine the instance that coverage data was originally generated for.
let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) {
self.monomorphize(inlined)
@@ -15,41 +14,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.instance
};
- let Coverage { kind, code_region } = coverage;
- match kind {
- CoverageKind::Counter { function_source_hash, id } => {
- if bx.set_function_source_hash(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(instance, id, code_region);
- }
-
- let coverageinfo = bx.tcx().coverageinfo(instance.def);
-
- let fn_name = bx.get_pgo_func_name_var(instance);
- let hash = bx.const_u64(function_source_hash);
- let num_counters = bx.const_u32(coverageinfo.num_counters);
- let index = bx.const_u32(id.zero_based_index());
- 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_coverage_counter_expression(instance, id, lhs, op, rhs, code_region);
- }
- CoverageKind::Unreachable => {
- bx.add_coverage_unreachable(
- instance,
- code_region.expect("unreachable regions always have code regions"),
- );
- }
- }
+ // Handle the coverage info in a backend-specific way.
+ bx.add_coverage(instance, coverage);
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index bba2800fb..1ee89b3d5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -5,6 +5,7 @@ use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
+use rustc_middle::ty::Ty;
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
@@ -248,7 +249,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
fn spill_operand_to_stack(
- operand: &OperandRef<'tcx, Bx::Value>,
+ operand: OperandRef<'tcx, Bx::Value>,
name: Option<String>,
bx: &mut Bx,
) -> PlaceRef<'tcx, Bx::Value> {
@@ -352,6 +353,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.set_var_name(a, &(name.clone() + ".0"));
bx.set_var_name(b, &(name.clone() + ".1"));
}
+ OperandValue::ZeroSized => {
+ // These never have a value to talk about
+ }
},
LocalRef::PendingOperand => {}
}
@@ -372,7 +376,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
return;
}
- Self::spill_operand_to_stack(operand, name, bx)
+ Self::spill_operand_to_stack(*operand, name, bx)
}
LocalRef::Place(place) => *place,
@@ -418,9 +422,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let create_alloca = |bx: &mut Bx, place: PlaceRef<'tcx, Bx::Value>, refcount| {
// Create a variable which will be a pointer to the actual value
- let ptr_ty = bx
- .tcx()
- .mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty });
+ let ptr_ty = Ty::new_ptr(
+ bx.tcx(),
+ ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: place.layout.ty },
+ );
let ptr_layout = bx.layout_of(ptr_ty);
let alloca = PlaceRef::alloca(bx, ptr_layout);
bx.set_var_name(alloca.llval, &format!("{}.ref{}.dbg.spill", var.name, refcount));
@@ -522,8 +527,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
for _ in 0..var.references {
- var_ty =
- bx.tcx().mk_ptr(ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty });
+ var_ty = Ty::new_ptr(
+ bx.tcx(),
+ ty::TypeAndMut { mutbl: mir::Mutability::Mut, ty: var_ty },
+ );
}
self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
@@ -547,7 +554,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if let Ok(operand) = self.eval_mir_constant_to_operand(bx, &c) {
self.set_debug_loc(bx, var.source_info);
let base = Self::spill_operand_to_stack(
- &operand,
+ operand,
Some(var.name.to_string()),
bx,
);
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 1479242f2..8a65dd593 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -211,68 +211,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
args[1].val.unaligned_volatile_store(bx, dst);
return;
}
- | sym::unchecked_div
- | sym::unchecked_rem
- | sym::unchecked_shl
- | sym::unchecked_shr
- | sym::unchecked_add
- | sym::unchecked_sub
- | sym::unchecked_mul
- | sym::exact_div => {
+ sym::exact_div => {
let ty = arg_tys[0];
match int_type_width_signed(ty, bx.tcx()) {
- Some((_width, signed)) => match name {
- sym::exact_div => {
- if signed {
- bx.exactsdiv(args[0].immediate(), args[1].immediate())
- } else {
- bx.exactudiv(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_div => {
- if signed {
- bx.sdiv(args[0].immediate(), args[1].immediate())
- } else {
- bx.udiv(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_rem => {
- if signed {
- bx.srem(args[0].immediate(), args[1].immediate())
- } else {
- bx.urem(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_shl => bx.shl(args[0].immediate(), args[1].immediate()),
- sym::unchecked_shr => {
- if signed {
- bx.ashr(args[0].immediate(), args[1].immediate())
- } else {
- bx.lshr(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_add => {
- if signed {
- bx.unchecked_sadd(args[0].immediate(), args[1].immediate())
- } else {
- bx.unchecked_uadd(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_sub => {
- if signed {
- bx.unchecked_ssub(args[0].immediate(), args[1].immediate())
- } else {
- bx.unchecked_usub(args[0].immediate(), args[1].immediate())
- }
- }
- sym::unchecked_mul => {
- if signed {
- bx.unchecked_smul(args[0].immediate(), args[1].immediate())
- } else {
- bx.unchecked_umul(args[0].immediate(), args[1].immediate())
- }
+ Some((_width, signed)) => {
+ if signed {
+ bx.exactsdiv(args[0].immediate(), args[1].immediate())
+ } else {
+ bx.exactudiv(args[0].immediate(), args[1].immediate())
}
- _ => bug!(),
},
None => {
bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty });
diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs
new file mode 100644
index 000000000..378c54013
--- /dev/null
+++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs
@@ -0,0 +1,75 @@
+//! Locals are in a private module as updating `LocalRef::Operand` has to
+//! be careful wrt to subtyping. To deal with this we only allow updates by using
+//! `FunctionCx::overwrite_local` which handles it automatically.
+use crate::mir::{FunctionCx, LocalRef};
+use crate::traits::BuilderMethods;
+use rustc_index::IndexVec;
+use rustc_middle::mir;
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use std::ops::{Index, IndexMut};
+pub(super) struct Locals<'tcx, V> {
+ values: IndexVec<mir::Local, LocalRef<'tcx, V>>,
+}
+
+impl<'tcx, V> Index<mir::Local> for Locals<'tcx, V> {
+ type Output = LocalRef<'tcx, V>;
+ #[inline]
+ fn index(&self, index: mir::Local) -> &LocalRef<'tcx, V> {
+ &self.values[index]
+ }
+}
+
+/// To mutate locals, use `FunctionCx::overwrite_local` instead.
+impl<'tcx, V, Idx: ?Sized> !IndexMut<Idx> for Locals<'tcx, V> {}
+
+impl<'tcx, V> Locals<'tcx, V> {
+ pub(super) fn empty() -> Locals<'tcx, V> {
+ Locals { values: IndexVec::default() }
+ }
+
+ pub(super) fn indices(&self) -> impl DoubleEndedIterator<Item = mir::Local> + Clone + 'tcx {
+ self.values.indices()
+ }
+}
+
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
+ pub(super) fn initialize_locals(&mut self, values: Vec<LocalRef<'tcx, Bx::Value>>) {
+ assert!(self.locals.values.is_empty());
+ // FIXME(#115215): After #115025 get's merged this might not be necessary
+ for (local, value) in values.into_iter().enumerate() {
+ match value {
+ LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
+ LocalRef::Operand(op) => {
+ let local = mir::Local::from_usize(local);
+ let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
+ if expected_ty != op.layout.ty {
+ warn!("Unexpected initial operand type. See the issues/114858");
+ }
+ }
+ }
+ self.locals.values.push(value);
+ }
+ }
+
+ pub(super) fn overwrite_local(
+ &mut self,
+ local: mir::Local,
+ mut value: LocalRef<'tcx, Bx::Value>,
+ ) {
+ match value {
+ LocalRef::Place(_) | LocalRef::UnsizedPlace(_) | LocalRef::PendingOperand => (),
+ LocalRef::Operand(ref mut op) => {
+ let local_ty = self.monomorphize(self.mir.local_decls[local].ty);
+ if local_ty != op.layout.ty {
+ // FIXME(#112651): This can be changed to an ICE afterwards.
+ debug!("updating type of operand due to subtyping");
+ with_no_trimmed_paths!(debug!(?op.layout.ty));
+ with_no_trimmed_paths!(debug!(?local_ty));
+ op.layout.ty = local_ty;
+ }
+ }
+ };
+
+ self.locals.values[local] = value;
+ }
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 1204c99e5..9ff6a2497 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -1,21 +1,31 @@
use crate::base;
use crate::traits::*;
+use rustc_index::bit_set::BitSet;
+use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
+use rustc_middle::mir::traversal;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_target::abi::call::{FnAbi, PassMode};
use std::iter;
-use rustc_index::bit_set::BitSet;
-use rustc_index::IndexVec;
+mod analyze;
+mod block;
+pub mod constant;
+pub mod coverageinfo;
+pub mod debuginfo;
+mod intrinsic;
+mod locals;
+pub mod operand;
+pub mod place;
+mod rvalue;
+mod statement;
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
-use self::place::PlaceRef;
-use rustc_middle::mir::traversal;
-
use self::operand::{OperandRef, OperandValue};
+use self::place::PlaceRef;
// Used for tracking the state of generated basic blocks.
enum CachedLlbb<T> {
@@ -91,7 +101,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
///
/// Avoiding allocs can also be important for certain intrinsics,
/// notably `expect`.
- locals: IndexVec<mir::Local, LocalRef<'tcx, Bx::Value>>,
+ locals: locals::Locals<'tcx, Bx::Value>,
/// All `VarDebugInfo` from the MIR body, partitioned by `Local`.
/// This is `None` if no var`#[non_exhaustive]`iable debuginfo/names are needed.
@@ -111,7 +121,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.instance.subst_mir_and_normalize_erasing_regions(
self.cx.tcx(),
ty::ParamEnv::reveal_all(),
- ty::EarlyBinder(value),
+ ty::EarlyBinder::bind(value),
)
}
}
@@ -129,16 +139,13 @@ enum LocalRef<'tcx, V> {
PendingOperand,
}
-impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
- fn new_operand<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
- bx: &mut Bx,
- layout: TyAndLayout<'tcx>,
- ) -> LocalRef<'tcx, V> {
+impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
+ fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> {
if layout.is_zst() {
// Zero-size temporaries aren't always initialized, which
// doesn't matter because they don't contain data, but
// we need something in the operand.
- LocalRef::Operand(OperandRef::new_zst(bx, layout))
+ LocalRef::Operand(OperandRef::zero_sized(layout))
} else {
LocalRef::PendingOperand
}
@@ -172,7 +179,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
start_bx.set_personality_fn(cx.eh_personality());
}
- let cleanup_kinds = base::wants_msvc_seh(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
+ let cleanup_kinds =
+ base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(&mir));
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
mir.basic_blocks
@@ -195,7 +203,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cleanup_kinds,
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
- locals: IndexVec::new(),
+ locals: locals::Locals::empty(),
debug_context,
per_local_var_debug_info: None,
caller_location: None,
@@ -226,7 +234,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let memory_locals = analyze::non_ssa_locals(&fx);
// Allocate variable and temp allocas
- fx.locals = {
+ let local_values = {
let args = arg_local_refs(&mut start_bx, &mut fx, &memory_locals);
let mut allocate_local = |local| {
@@ -249,7 +257,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
} else {
debug!("alloc: {:?} -> operand", local);
- LocalRef::new_operand(&mut start_bx, layout)
+ LocalRef::new_operand(layout)
}
};
@@ -259,6 +267,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
.chain(mir.vars_and_temps_iter().map(allocate_local))
.collect()
};
+ fx.initialize_locals(local_values);
// Apply debuginfo to the newly allocated locals.
fx.debug_introduce_locals(&mut start_bx);
@@ -292,14 +301,13 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
.enumerate()
.map(|(arg_index, local)| {
let arg_decl = &mir.local_decls[local];
+ let arg_ty = fx.monomorphize(arg_decl.ty);
if Some(local) == 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 LLVM function arguments.
-
- let arg_ty = fx.monomorphize(arg_decl.ty);
let ty::Tuple(tupled_arg_tys) = arg_ty.kind() else {
bug!("spread argument isn't a tuple?!");
};
@@ -334,8 +342,6 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
}
if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() {
- let arg_ty = fx.monomorphize(arg_decl.ty);
-
let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty));
bx.va_start(va_list.llval);
@@ -355,7 +361,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let local = |op| LocalRef::Operand(op);
match arg.mode {
PassMode::Ignore => {
- return local(OperandRef::new_zst(bx, arg.layout));
+ return local(OperandRef::zero_sized(arg.layout));
}
PassMode::Direct(_) => {
let llarg = bx.get_param(llarg_idx);
@@ -432,14 +438,3 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
args
}
-
-mod analyze;
-mod block;
-pub mod constant;
-pub mod coverageinfo;
-pub mod debuginfo;
-mod intrinsic;
-pub mod operand;
-pub mod place;
-mod rvalue;
-mod statement;
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 2301c3ef1..31c293d7c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -8,10 +8,10 @@ use crate::traits::*;
use crate::MemFlags;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ConstValue, Pointer, Scalar};
+use rustc_middle::mir::interpret::{alloc_range, ConstValue, Pointer, Scalar};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::Ty;
-use rustc_target::abi::{Abi, Align, Size};
+use rustc_target::abi::{self, Abi, Align, Size};
use std::fmt;
@@ -45,6 +45,14 @@ pub enum OperandValue<V> {
/// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`]
/// with `immediate: true`.
Pair(V, V),
+ /// A value taking no bytes, and which therefore needs no LLVM value at all.
+ ///
+ /// If you ever need a `V` to pass to something, get a fresh poison value
+ /// from [`ConstMethods::const_poison`].
+ ///
+ /// An `OperandValue` *must* be this variant for any type for which
+ /// `is_zst` on its `Layout` returns `true`.
+ ZeroSized,
}
/// An `OperandRef` is an "SSA" reference to a Rust value, along with
@@ -71,15 +79,9 @@ impl<V: CodegenObject> fmt::Debug for OperandRef<'_, V> {
}
impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
- pub fn new_zst<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
- bx: &mut Bx,
- layout: TyAndLayout<'tcx>,
- ) -> OperandRef<'tcx, V> {
+ pub fn zero_sized(layout: TyAndLayout<'tcx>) -> OperandRef<'tcx, V> {
assert!(layout.is_zst());
- OperandRef {
- val: OperandValue::Immediate(bx.const_poison(bx.immediate_backend_type(layout))),
- layout,
- }
+ OperandRef { val: OperandValue::ZeroSized, layout }
}
pub fn from_const<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
@@ -97,7 +99,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let llval = bx.scalar_to_backend(x, scalar, bx.immediate_backend_type(layout));
OperandValue::Immediate(llval)
}
- ConstValue::ZeroSized => return OperandRef::new_zst(bx, layout),
+ ConstValue::ZeroSized => return OperandRef::zero_sized(layout),
ConstValue::Slice { data, start, end } => {
let Abi::ScalarPair(a_scalar, _) = layout.abi else {
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
@@ -115,13 +117,82 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
OperandValue::Pair(a_llval, b_llval)
}
ConstValue::ByRef { alloc, offset } => {
- return bx.load_operand(bx.from_const_alloc(layout, alloc, offset));
+ return Self::from_const_alloc(bx, layout, alloc, offset);
}
};
OperandRef { val, layout }
}
+ fn from_const_alloc<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
+ bx: &mut Bx,
+ layout: TyAndLayout<'tcx>,
+ alloc: rustc_middle::mir::interpret::ConstAllocation<'tcx>,
+ offset: Size,
+ ) -> Self {
+ let alloc_align = alloc.inner().align;
+ assert_eq!(alloc_align, layout.align.abi);
+ let ty = bx.type_ptr_to(bx.cx().backend_type(layout));
+
+ let read_scalar = |start, size, s: abi::Scalar, ty| {
+ let val = alloc
+ .0
+ .read_scalar(
+ bx,
+ alloc_range(start, size),
+ /*read_provenance*/ matches!(s.primitive(), abi::Pointer(_)),
+ )
+ .unwrap();
+ bx.scalar_to_backend(val, s, ty)
+ };
+
+ // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
+ // However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
+ // and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
+ // case where some of the bytes are initialized and others are not. So, we need an extra
+ // check that walks over the type of `mplace` to make sure it is truly correct to treat this
+ // like a `Scalar` (or `ScalarPair`).
+ match layout.abi {
+ Abi::Scalar(s @ abi::Scalar::Initialized { .. }) => {
+ let size = s.size(bx);
+ assert_eq!(size, layout.size, "abi::Scalar size does not match layout size");
+ let val = read_scalar(Size::ZERO, size, s, ty);
+ OperandRef { val: OperandValue::Immediate(val), layout }
+ }
+ Abi::ScalarPair(
+ a @ abi::Scalar::Initialized { .. },
+ b @ abi::Scalar::Initialized { .. },
+ ) => {
+ let (a_size, b_size) = (a.size(bx), b.size(bx));
+ let b_offset = a_size.align_to(b.align(bx).abi);
+ assert!(b_offset.bytes() > 0);
+ let a_val = read_scalar(
+ Size::ZERO,
+ a_size,
+ a,
+ bx.scalar_pair_element_backend_type(layout, 0, true),
+ );
+ let b_val = read_scalar(
+ b_offset,
+ b_size,
+ b,
+ bx.scalar_pair_element_backend_type(layout, 1, true),
+ );
+ OperandRef { val: OperandValue::Pair(a_val, b_val), layout }
+ }
+ _ if layout.is_zst() => OperandRef::zero_sized(layout),
+ _ => {
+ // Neither a scalar nor scalar pair. Load from a place
+ let init = bx.const_data_from_alloc(alloc);
+ let base_addr = bx.static_addr_of(init, alloc_align, None);
+
+ let llval = bx.const_ptr_byte_offset(base_addr, offset);
+ let llval = bx.const_bitcast(llval, ty);
+ bx.load_operand(PlaceRef::new_sized(llval, layout))
+ }
+ }
+ }
+
/// Asserts that this operand refers to a scalar and returns
/// a reference to its value.
pub fn immediate(self) -> V {
@@ -147,6 +218,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
OperandValue::Immediate(llptr) => (llptr, None),
OperandValue::Pair(llptr, llextra) => (llptr, Some(llextra)),
OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self),
+ OperandValue::ZeroSized => bug!("Deref of ZST operand {:?}", self),
};
let layout = cx.layout_of(projected_ty);
PlaceRef { llval: llptr, llextra, layout, align: layout.align.abi }
@@ -204,9 +276,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let mut val = match (self.val, self.layout.abi) {
// If the field is ZST, it has no data.
- _ if field.is_zst() => {
- return OperandRef::new_zst(bx, field);
- }
+ _ if field.is_zst() => OperandValue::ZeroSized,
// Newtype of a scalar, scalar pair or vector.
(OperandValue::Immediate(_) | OperandValue::Pair(..), _)
@@ -237,6 +307,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
};
match (&mut val, field.abi) {
+ (OperandValue::ZeroSized, _) => {}
(
OperandValue::Immediate(llval),
Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. },
@@ -290,8 +361,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
/// Returns an `OperandValue` that's generally UB to use in any way.
///
- /// Depending on the `layout`, returns an `Immediate` or `Pair` containing
- /// poison value(s), or a `Ref` containing a poison pointer.
+ /// Depending on the `layout`, returns `ZeroSized` for ZSTs, an `Immediate` or
+ /// `Pair` containing poison value(s), or a `Ref` containing a poison pointer.
///
/// Supports sized types only.
pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
@@ -299,7 +370,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
layout: TyAndLayout<'tcx>,
) -> OperandValue<V> {
assert!(layout.is_sized());
- if bx.cx().is_backend_immediate(layout) {
+ if layout.is_zst() {
+ OperandValue::ZeroSized
+ } else if bx.cx().is_backend_immediate(layout) {
let ibty = bx.cx().immediate_backend_type(layout);
OperandValue::Immediate(bx.const_poison(ibty))
} else if bx.cx().is_backend_scalar_pair(layout) {
@@ -352,12 +425,11 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
flags: MemFlags,
) {
debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest);
- // Avoid generating stores of zero-sized values, because the only way to have a zero-sized
- // value is through `undef`, and store itself is useless.
- if dest.layout.is_zst() {
- return;
- }
match self {
+ OperandValue::ZeroSized => {
+ // Avoid generating stores of zero-sized values, because the only way to have a zero-sized
+ // value is through `undef`/`poison`, and the store itself is useless.
+ }
OperandValue::Ref(r, None, source_align) => {
if flags.contains(MemFlags::NONTEMPORAL) {
// HACK(nox): This is inefficient but there is no nontemporal memcpy.
@@ -458,7 +530,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// checks in `codegen_consume` and `extract_field`.
let elem = o.layout.field(bx.cx(), 0);
if elem.is_zst() {
- o = OperandRef::new_zst(bx, elem);
+ o = OperandRef::zero_sized(elem);
} else {
return None;
}
@@ -492,7 +564,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// ZSTs don't require any actual memory access.
if layout.is_zst() {
- return OperandRef::new_zst(bx, layout);
+ return OperandRef::zero_sized(layout);
}
if let Some(o) = self.maybe_codegen_consume_direct(bx, place_ref) {
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index a58a61cd5..ab493ae5c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -61,7 +61,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
layout: TyAndLayout<'tcx>,
) -> Self {
assert!(layout.is_unsized(), "tried to allocate indirect place for sized values");
- let ptr_ty = bx.cx().tcx().mk_mut_ptr(layout.ty);
+ let ptr_ty = Ty::new_mut_ptr(bx.cx().tcx(), layout.ty);
let ptr_layout = bx.cx().layout_of(ptr_ty);
Self::alloca(bx, ptr_layout)
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 6e7065713..956f03d25 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -11,7 +11,7 @@ use rustc_middle::mir;
use rustc_middle::mir::Operand;
use rustc_middle::ty::cast::{CastTy, IntTy};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
-use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, Ty, TyCtxt};
use rustc_session::config::OptLevel;
use rustc_span::source_map::{Span, DUMMY_SP};
use rustc_target::abi::{self, FIRST_VARIANT};
@@ -32,7 +32,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
cg_operand.val.store(bx, dest);
}
- mir::Rvalue::Cast(mir::CastKind::Pointer(PointerCast::Unsize), ref source, _) => {
+ mir::Rvalue::Cast(
+ mir::CastKind::PointerCoercion(PointerCoercion::Unsize),
+ ref source,
+ _,
+ ) => {
// The destination necessarily contains a fat pointer, so if
// it's a scalar pair, it's a fat pointer or newtype thereof.
if bx.cx().is_backend_scalar_pair(dest.layout) {
@@ -70,6 +74,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandValue::Ref(_, Some(_), _) => {
bug!("unsized coercion on an unsized rvalue");
}
+ OperandValue::ZeroSized => {
+ bug!("unsized coercion on a ZST rvalue");
+ }
}
}
@@ -165,11 +172,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
match src.val {
- OperandValue::Ref(..) => {
+ OperandValue::Ref(..) | OperandValue::ZeroSized => {
span_bug!(
self.mir.span,
"Operand path should have handled transmute \
- from `Ref` {src:?} to place {dst:?}"
+ from {src:?} to place {dst:?}"
);
}
OperandValue::Immediate(..) | OperandValue::Pair(..) => {
@@ -220,17 +227,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let fake_place = PlaceRef::new_sized_aligned(cast_ptr, cast, align);
Some(bx.load_operand(fake_place).val)
}
+ OperandValue::ZeroSized => {
+ let OperandValueKind::ZeroSized = operand_kind else {
+ bug!("Found {operand_kind:?} for operand {operand:?}");
+ };
+ if let OperandValueKind::ZeroSized = cast_kind {
+ Some(OperandValue::ZeroSized)
+ } else {
+ None
+ }
+ }
OperandValue::Immediate(imm) => {
let OperandValueKind::Immediate(in_scalar) = operand_kind else {
bug!("Found {operand_kind:?} for operand {operand:?}");
};
- if let OperandValueKind::Immediate(out_scalar) = cast_kind {
- match (in_scalar, out_scalar) {
- (ScalarOrZst::Zst, ScalarOrZst::Zst) => {
- Some(OperandRef::new_zst(bx, cast).val)
- }
- (ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar))
- if in_scalar.size(self.cx) == out_scalar.size(self.cx) =>
+ if let OperandValueKind::Immediate(out_scalar) = cast_kind
+ && in_scalar.size(self.cx) == out_scalar.size(self.cx)
{
let operand_bty = bx.backend_type(operand.layout);
let cast_bty = bx.backend_type(cast);
@@ -242,9 +254,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
out_scalar,
cast_bty,
)))
- }
- _ => None,
- }
} else {
None
}
@@ -406,7 +415,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let lladdr = bx.ptrtoint(llptr, llcast_ty);
OperandValue::Immediate(lladdr)
}
- mir::CastKind::Pointer(PointerCast::ReifyFnPointer) => {
+ mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
match *operand.layout.ty.kind() {
ty::FnDef(def_id, substs) => {
let instance = ty::Instance::resolve_for_fn_ptr(
@@ -422,7 +431,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => bug!("{} cannot be reified to a fn ptr", operand.layout.ty),
}
}
- mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)) => {
+ mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => {
match *operand.layout.ty.kind() {
ty::Closure(def_id, substs) => {
let instance = Instance::resolve_closure(
@@ -438,11 +447,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
_ => bug!("{} cannot be cast to a fn ptr", operand.layout.ty),
}
}
- mir::CastKind::Pointer(PointerCast::UnsafeFnPointer) => {
+ mir::CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => {
// This is a no-op at the LLVM level.
operand.val
}
- mir::CastKind::Pointer(PointerCast::Unsize) => {
+ mir::CastKind::PointerCoercion(PointerCoercion::Unsize) => {
assert!(bx.cx().is_backend_scalar_pair(cast));
let (lldata, llextra) = match operand.val {
OperandValue::Pair(lldata, llextra) => {
@@ -457,12 +466,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandValue::Ref(..) => {
bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
}
+ OperandValue::ZeroSized => {
+ bug!("zero-sized operand {:?} in `codegen_rvalue_operand`", operand);
+ }
};
let (lldata, llextra) =
base::unsize_ptr(bx, lldata, operand.layout.ty, cast.ty, llextra);
OperandValue::Pair(lldata, llextra)
}
- mir::CastKind::Pointer(PointerCast::MutToConstPointer)
+ mir::CastKind::PointerCoercion(PointerCoercion::MutToConstPointer)
| mir::CastKind::PtrToPtr
if bx.cx().is_backend_scalar_pair(operand.layout) =>
{
@@ -490,13 +502,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
OperandValue::Ref(_, _, _) => todo!(),
OperandValue::Immediate(v) => (v, None),
OperandValue::Pair(v, l) => (v, Some(l)),
+ OperandValue::ZeroSized => bug!("ZST -- which is not PointerLike -- in DynStar"),
};
let (lldata, llextra) =
base::cast_to_dyn_star(bx, lldata, operand.layout, cast.ty, llextra);
OperandValue::Pair(lldata, llextra)
}
- mir::CastKind::Pointer(
- PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
+ mir::CastKind::PointerCoercion(
+ PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
)
| mir::CastKind::IntToInt
| mir::CastKind::FloatToInt
@@ -572,7 +585,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::Ref(_, bk, place) => {
let mk_ref = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
- tcx.mk_ref(
+ Ty::new_ref(
+ tcx,
tcx.lifetimes.re_erased,
ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() },
)
@@ -583,7 +597,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::CopyForDeref(place) => self.codegen_operand(bx, &Operand::Copy(place)),
mir::Rvalue::AddressOf(mutability, place) => {
let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| {
- tcx.mk_ptr(ty::TypeAndMut { ty, mutbl: mutability })
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl: mutability })
};
self.codegen_place_to_pointer(bx, place, mk_ptr)
}
@@ -635,7 +649,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
lhs.layout.ty,
);
let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty);
- let operand_ty = bx.tcx().mk_tup(&[val_ty, bx.tcx().types.bool]);
+ let operand_ty = Ty::new_tup(bx.tcx(), &[val_ty, bx.tcx().types.bool]);
OperandRef { val: result, layout: bx.cx().layout_of(operand_ty) }
}
@@ -668,11 +682,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::NullaryOp(ref null_op, ty) => {
let ty = self.monomorphize(ty);
- assert!(bx.cx().type_is_sized(ty));
let layout = bx.cx().layout_of(ty);
let val = match null_op {
- mir::NullOp::SizeOf => layout.size.bytes(),
- mir::NullOp::AlignOf => layout.align.abi.bytes(),
+ mir::NullOp::SizeOf => {
+ assert!(bx.cx().type_is_sized(ty));
+ layout.size.bytes()
+ }
+ mir::NullOp::AlignOf => {
+ assert!(bx.cx().type_is_sized(ty));
+ layout.align.abi.bytes()
+ }
mir::NullOp::OffsetOf(fields) => {
layout.offset_of_subfield(bx.cx(), fields.iter().map(|f| f.index())).bytes()
}
@@ -713,14 +732,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// According to `rvalue_creates_operand`, only ZST
// aggregate rvalues are allowed to be operands.
let ty = rvalue.ty(self.mir, self.cx.tcx());
- OperandRef::new_zst(bx, self.cx.layout_of(self.monomorphize(ty)))
+ OperandRef::zero_sized(self.cx.layout_of(self.monomorphize(ty)))
}
mir::Rvalue::ShallowInitBox(ref operand, content_ty) => {
let operand = self.codegen_operand(bx, operand);
let lloperand = operand.immediate();
let content_ty = self.monomorphize(content_ty);
- let box_layout = bx.cx().layout_of(bx.tcx().mk_box(content_ty));
+ let box_layout = bx.cx().layout_of(Ty::new_box(bx.tcx(), content_ty));
let llty_ptr = bx.cx().backend_type(box_layout);
let val = bx.pointercast(lloperand, llty_ptr);
@@ -784,6 +803,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.add(lhs, rhs)
}
}
+ mir::BinOp::AddUnchecked => {
+ if is_signed {
+ bx.unchecked_sadd(lhs, rhs)
+ } else {
+ bx.unchecked_uadd(lhs, rhs)
+ }
+ }
mir::BinOp::Sub => {
if is_float {
bx.fsub(lhs, rhs)
@@ -791,6 +817,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.sub(lhs, rhs)
}
}
+ mir::BinOp::SubUnchecked => {
+ if is_signed {
+ bx.unchecked_ssub(lhs, rhs)
+ } else {
+ bx.unchecked_usub(lhs, rhs)
+ }
+ }
mir::BinOp::Mul => {
if is_float {
bx.fmul(lhs, rhs)
@@ -798,6 +831,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.mul(lhs, rhs)
}
}
+ mir::BinOp::MulUnchecked => {
+ if is_signed {
+ bx.unchecked_smul(lhs, rhs)
+ } else {
+ bx.unchecked_umul(lhs, rhs)
+ }
+ }
mir::BinOp::Div => {
if is_float {
bx.fdiv(lhs, rhs)
@@ -834,8 +874,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.inbounds_gep(llty, lhs, &[rhs])
}
}
- mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs),
- mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs),
+ mir::BinOp::Shl => common::build_masked_lshift(bx, lhs, rhs),
+ mir::BinOp::ShlUnchecked => {
+ let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+ bx.shl(lhs, rhs)
+ }
+ mir::BinOp::Shr => common::build_masked_rshift(bx, input_ty, lhs, rhs),
+ mir::BinOp::ShrUnchecked => {
+ let rhs = base::cast_shift_expr_rhs(bx, lhs, rhs);
+ if is_signed { bx.ashr(lhs, rhs) } else { bx.lshr(lhs, rhs) }
+ }
mir::BinOp::Ne
| mir::BinOp::Lt
| mir::BinOp::Gt
@@ -931,6 +979,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Can always load from a pointer as needed
(OperandValueKind::Ref, _) => true,
+ // ZST-to-ZST is the easiest thing ever
+ (OperandValueKind::ZeroSized, OperandValueKind::ZeroSized) => true,
+
+ // But if only one of them is a ZST the sizes can't match
+ (OperandValueKind::ZeroSized, _) | (_, OperandValueKind::ZeroSized) => false,
+
// Need to generate an `alloc` to get a pointer from an immediate
(OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
@@ -974,12 +1028,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
/// Gets which variant of [`OperandValue`] is expected for a particular type.
fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind {
- if self.cx.is_backend_immediate(layout) {
+ if layout.is_zst() {
+ OperandValueKind::ZeroSized
+ } else if self.cx.is_backend_immediate(layout) {
debug_assert!(!self.cx.is_backend_scalar_pair(layout));
OperandValueKind::Immediate(match layout.abi {
- abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s),
- abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element),
- _ if layout.is_zst() => ScalarOrZst::Zst,
+ abi::Abi::Scalar(s) => s,
+ abi::Abi::Vector { element, .. } => element,
x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
})
} else if self.cx.is_backend_scalar_pair(layout) {
@@ -1002,21 +1057,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[derive(Debug, Copy, Clone)]
enum OperandValueKind {
Ref,
- Immediate(ScalarOrZst),
+ Immediate(abi::Scalar),
Pair(abi::Scalar, abi::Scalar),
-}
-
-#[derive(Debug, Copy, Clone)]
-enum ScalarOrZst {
- Zst,
- Scalar(abi::Scalar),
-}
-
-impl ScalarOrZst {
- pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size {
- match self {
- ScalarOrZst::Zst => abi::Size::ZERO,
- ScalarOrZst::Scalar(s) => s.size(cx),
- }
- }
+ ZeroSized,
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 3fd7397ad..899e41265 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -20,7 +20,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
LocalRef::PendingOperand => {
let operand = self.codegen_rvalue_operand(bx, rvalue);
- self.locals[index] = LocalRef::Operand(operand);
+ self.overwrite_local(index, LocalRef::Operand(operand));
self.debug_introduce_local(bx, index);
}
LocalRef::Operand(op) => {
@@ -65,7 +65,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
mir::StatementKind::Coverage(box ref coverage) => {
- self.codegen_coverage(bx, coverage.clone(), statement.source_info.scope);
+ self.codegen_coverage(bx, coverage, statement.source_info.scope);
}
mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => {
let op_val = self.codegen_operand(bx, op);
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index c5976a654..9e06fec55 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -44,6 +44,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
// #[target_feature].
("thumb-mode", Some(sym::arm_target_feature)),
("thumb2", Some(sym::arm_target_feature)),
+ ("trustzone", Some(sym::arm_target_feature)),
("v5te", Some(sym::arm_target_feature)),
("v6", Some(sym::arm_target_feature)),
("v6k", Some(sym::arm_target_feature)),
@@ -53,6 +54,7 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
("vfp2", Some(sym::arm_target_feature)),
("vfp3", Some(sym::arm_target_feature)),
("vfp4", Some(sym::arm_target_feature)),
+ ("virtualization", Some(sym::arm_target_feature)),
// tidy-alphabetical-end
];
@@ -282,6 +284,7 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
// tidy-alphabetical-start
("atomics", Some(sym::wasm_target_feature)),
("bulk-memory", Some(sym::wasm_target_feature)),
+ ("exception-handling", Some(sym::wasm_target_feature)),
("multivalue", Some(sym::wasm_target_feature)),
("mutable-globals", Some(sym::wasm_target_feature)),
("nontrapping-fptoint", Some(sym::wasm_target_feature)),
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index d83bfc740..b3c9ecf8b 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -6,7 +6,7 @@ use crate::back::write::TargetMachineFactoryFn;
use crate::{CodegenResults, ModuleCodegen};
use rustc_ast::expand::allocator::AllocatorKind;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_errors::ErrorGuaranteed;
use rustc_metadata::EncodedMetadata;
@@ -101,7 +101,7 @@ pub trait CodegenBackend {
ongoing_codegen: Box<dyn Any>,
sess: &Session,
outputs: &OutputFilenames,
- ) -> Result<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>), ErrorGuaranteed>;
+ ) -> Result<(CodegenResults, FxIndexMap<WorkProductId, WorkProduct>), ErrorGuaranteed>;
/// This is called on the returned `Box<dyn Any>` from `join_codegen`
///
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index 619063027..d6e9bfce1 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -1,8 +1,6 @@
use super::BackendTypes;
-use crate::mir::place::PlaceRef;
use rustc_middle::mir::interpret::{ConstAllocation, Scalar};
-use rustc_middle::ty::layout::TyAndLayout;
-use rustc_target::abi::{self, Size};
+use rustc_target::abi;
pub trait ConstMethods<'tcx>: BackendTypes {
// Constant constructors
@@ -17,6 +15,7 @@ pub trait ConstMethods<'tcx>: BackendTypes {
fn const_i32(&self, i: i32) -> Self::Value;
fn const_u32(&self, i: u32) -> Self::Value;
fn const_u64(&self, i: u64) -> Self::Value;
+ fn const_u128(&self, i: u128) -> Self::Value;
fn const_usize(&self, i: u64) -> Self::Value;
fn const_u8(&self, i: u8) -> Self::Value;
fn const_real(&self, t: Self::Type, val: f64) -> Self::Value;
@@ -30,12 +29,8 @@ pub trait ConstMethods<'tcx>: BackendTypes {
fn const_data_from_alloc(&self, alloc: ConstAllocation<'tcx>) -> Self::Value;
fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value;
- fn from_const_alloc(
- &self,
- layout: TyAndLayout<'tcx>,
- alloc: ConstAllocation<'tcx>,
- offset: Size,
- ) -> PlaceRef<'tcx, Self::Value>;
fn const_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
+ fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
+ fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value;
}
diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
index e77201cf0..7e8de0ddc 100644
--- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs
@@ -1,57 +1,11 @@
use super::BackendTypes;
-use rustc_hir::def_id::DefId;
-use rustc_middle::mir::coverage::*;
+use rustc_middle::mir::Coverage;
use rustc_middle::ty::Instance;
-pub trait CoverageInfoMethods<'tcx>: BackendTypes {
- fn coverageinfo_finalize(&self);
-
- /// Codegen a small function that will never be called, with one counter
- /// that will never be incremented, that gives LLVM coverage tools a
- /// function definition it needs in order to resolve coverage map references
- /// to unused functions. This is necessary so unused functions will appear
- /// as uncovered (coverage execution count `0`) in LLVM coverage reports.
- fn define_unused_fn(&self, def_id: DefId);
-
- /// For LLVM codegen, returns a function-specific `Value` for a global
- /// string, to hold the function name passed to LLVM intrinsic
- /// `instrprof.increment()`. The `Value` is only created once per instance.
- /// Multiple invocations with the same instance return the same `Value`.
- fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value;
-}
-
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
- /// Returns true if the function source hash was added to the coverage map (even if it had
- /// already been added, for this instance). Returns false *only* if `-C 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,
- ) -> bool;
-
- /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage`
- /// is not enabled (a coverage map is not being generated).
- fn add_coverage_counter(
- &mut self,
- instance: Instance<'tcx>,
- index: CounterValueReference,
- region: CodeRegion,
- ) -> bool;
-
- /// Returns true if the expression was added to the coverage map; false if
- /// `-C instrument-coverage` is not enabled (a coverage map is not being generated).
- fn add_coverage_counter_expression(
- &mut self,
- instance: Instance<'tcx>,
- id: InjectedExpressionId,
- lhs: ExpressionOperandId,
- op: Op,
- rhs: ExpressionOperandId,
- region: Option<CodeRegion>,
- ) -> bool;
-
- /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage`
- /// is not enabled (a coverage map is not being generated).
- fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool;
+ /// Handle the MIR coverage info in a backend-specific way.
+ ///
+ /// This can potentially be a no-op in backends that don't support
+ /// coverage instrumentation.
+ fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage);
}
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 782fdadbf..8cb58bd4c 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -33,7 +33,7 @@ pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAs
pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
pub use self::builder::{BuilderMethods, OverflowOp};
pub use self::consts::ConstMethods;
-pub use self::coverageinfo::{CoverageInfoBuilderMethods, CoverageInfoMethods};
+pub use self::coverageinfo::CoverageInfoBuilderMethods;
pub use self::debuginfo::{DebugInfoBuilderMethods, DebugInfoMethods};
pub use self::declare::PreDefineMethods;
pub use self::intrinsic::IntrinsicCallMethods;
@@ -59,7 +59,6 @@ pub trait CodegenMethods<'tcx>:
+ MiscMethods<'tcx>
+ ConstMethods<'tcx>
+ StaticMethods
- + CoverageInfoMethods<'tcx>
+ DebugInfoMethods<'tcx>
+ AsmMethods<'tcx>
+ PreDefineMethods<'tcx>
@@ -75,7 +74,6 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where
+ MiscMethods<'tcx>
+ ConstMethods<'tcx>
+ StaticMethods
- + CoverageInfoMethods<'tcx>
+ DebugInfoMethods<'tcx>
+ AsmMethods<'tcx>
+ PreDefineMethods<'tcx>
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 36d986422..e64417e1a 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -126,6 +126,28 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
index: usize,
immediate: bool,
) -> Self::Type;
+
+ /// A type that can be used in a [`super::BuilderMethods::load`] +
+ /// [`super::BuilderMethods::store`] pair to implement a *typed* copy,
+ /// such as a MIR `*_0 = *_1`.
+ ///
+ /// It's always legal to return `None` here, as the provided impl does,
+ /// in which case callers should use [`super::BuilderMethods::memcpy`]
+ /// instead of the `load`+`store` pair.
+ ///
+ /// This can be helpful for things like arrays, where the LLVM backend type
+ /// `[3 x i16]` optimizes to three separate loads and stores, but it can
+ /// instead be copied via an `i48` that stays as the single `load`+`store`.
+ /// (As of 2023-05 LLVM cannot necessarily optimize away a `memcpy` in these
+ /// cases, due to `poison` handling, but in codegen we have more information
+ /// about the type invariants, so can emit something better instead.)
+ ///
+ /// This *should* return `None` for particularly-large types, where leaving
+ /// the `memcpy` may well be important to avoid code size explosion.
+ fn scalar_copy_backend_type(&self, layout: TyAndLayout<'tcx>) -> Option<Self::Type> {
+ let _ = layout;
+ None
+ }
}
// For backends that support CFI using type membership (i.e., testing whether a given pointer is
diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl
index 7d56cf0aa..e99005316 100644
--- a/compiler/rustc_const_eval/messages.ftl
+++ b/compiler/rustc_const_eval/messages.ftl
@@ -1,8 +1,142 @@
+const_eval_address_space_full =
+ there are no more free addresses in the address space
+const_eval_align_check_failed = accessing memory with alignment {$has}, but alignment {$required} is required
+const_eval_align_offset_invalid_align =
+ `align_offset` called with non-power-of-two align: {$target_align}
+
+const_eval_alignment_check_failed =
+ accessing memory with alignment {$has}, but alignment {$required} is required
+const_eval_already_reported =
+ an error has already been reported elsewhere (this should not usually be printed)
+const_eval_assume_false =
+ `assume` called with `false`
+
+const_eval_await_non_const =
+ cannot convert `{$ty}` into a future in {const_eval_const_context}s
+const_eval_bounds_check_failed =
+ indexing out of bounds: the len is {$len} but the index is {$index}
+const_eval_box_to_mut = {$front_matter}: encountered a box pointing to mutable memory in a constant
+const_eval_box_to_static = {$front_matter}: encountered a box pointing to a static variable in a constant
+const_eval_box_to_uninhabited = {$front_matter}: encountered a box pointing to uninhabited type {$ty}
+const_eval_call_nonzero_intrinsic =
+ `{$name}` called on 0
+
+const_eval_closure_call =
+ closures need an RFC before allowed to be called in {const_eval_const_context}s
+const_eval_closure_fndef_not_const =
+ function defined here, but it is not `const`
+const_eval_closure_non_const =
+ cannot call non-const closure in {const_eval_const_context}s
+const_eval_consider_dereferencing =
+ consider dereferencing here
+const_eval_const_accesses_static = constant accesses static
+
+const_eval_const_context = {$kind ->
+ [const] constant
+ [static] static
+ [const_fn] constant function
+ *[other] {""}
+}
+
+const_eval_copy_nonoverlapping_overlapping =
+ `copy_nonoverlapping` called on overlapping ranges
+
+const_eval_dangling_box_no_provenance = {$front_matter}: encountered a dangling box ({$pointer} has no provenance)
+const_eval_dangling_box_out_of_bounds = {$front_matter}: encountered a dangling box (going beyond the bounds of its allocation)
+const_eval_dangling_box_use_after_free = {$front_matter}: encountered a dangling box (use-after-free)
+const_eval_dangling_int_pointer =
+ {$bad_pointer_message}: {$pointer} is a dangling pointer (it has no provenance)
+const_eval_dangling_null_pointer =
+ {$bad_pointer_message}: null pointer is a dangling pointer (it has no provenance)
+const_eval_dangling_ptr_in_final = encountered dangling pointer in final constant
+
+const_eval_dangling_ref_no_provenance = {$front_matter}: encountered a dangling reference ({$pointer} has no provenance)
+const_eval_dangling_ref_out_of_bounds = {$front_matter}: encountered a dangling reference (going beyond the bounds of its allocation)
+const_eval_dangling_ref_use_after_free = {$front_matter}: encountered a dangling reference (use-after-free)
+const_eval_dead_local =
+ accessing a dead local variable
+const_eval_dealloc_immutable =
+ deallocating immutable allocation {$alloc}
+
+const_eval_dealloc_incorrect_layout =
+ incorrect layout on deallocation: {$alloc} has size {$size} and alignment {$align}, but gave size {$size_found} and alignment {$align_found}
+
+const_eval_dealloc_kind_mismatch =
+ deallocating {$alloc}, which is {$alloc_kind} memory, using {$kind} deallocation operation
+
+const_eval_deref_coercion_non_const =
+ cannot perform deref coercion on `{$ty}` in {const_eval_const_context}s
+ .note = attempting to deref into `{$target_ty}`
+ .target_note = deref defined here
+const_eval_deref_function_pointer =
+ accessing {$allocation} which contains a function
+const_eval_deref_test = dereferencing pointer failed
+const_eval_deref_vtable_pointer =
+ accessing {$allocation} which contains a vtable
+const_eval_different_allocations =
+ `{$name}` called on pointers into different allocations
+
+const_eval_division_by_zero =
+ dividing by zero
+const_eval_division_overflow =
+ overflow in signed division (dividing MIN by -1)
+const_eval_double_storage_live =
+ StorageLive on a local that was already live
+
+const_eval_dyn_call_not_a_method =
+ `dyn` call trying to call something that is not a method
+
+const_eval_dyn_call_vtable_mismatch =
+ `dyn` call on a pointer whose vtable does not match its type
+
+const_eval_dyn_star_call_vtable_mismatch =
+ `dyn*` call on a pointer whose vtable does not match its type
+
+const_eval_erroneous_constant =
+ erroneous constant used
+
+const_eval_error = {$error_kind ->
+ [static] could not evaluate static initializer
+ [const] evaluation of constant value failed
+ [const_with_path] evaluation of `{$instance}` failed
+ *[other] {""}
+}
+
+const_eval_exact_div_has_remainder =
+ exact_div: {$a} cannot be divided by {$b} without remainder
+
+const_eval_expected_non_ptr = {$front_matter}: encountered `{$value}`, but expected plain (non-pointer) bytes
+const_eval_fn_ptr_call =
+ function pointers need an RFC before allowed to be called in {const_eval_const_context}s
+const_eval_for_loop_into_iter_non_const =
+ cannot convert `{$ty}` into an iterator in {const_eval_const_context}s
+
+const_eval_frame_note = {$times ->
+ [0] {const_eval_frame_note_inner}
+ *[other] [... {$times} additional calls {const_eval_frame_note_inner} ...]
+}
+
+const_eval_frame_note_inner = inside {$where_ ->
+ [closure] closure
+ [instance] `{$instance}`
+ *[other] {""}
+}
+
+const_eval_in_bounds_test = out-of-bounds pointer use
+const_eval_incompatible_calling_conventions =
+ calling a function with calling convention {$callee_conv} using calling convention {$caller_conv}
+
+const_eval_incompatible_return_types =
+ calling a function with return type {$callee_ty} passing return place of type {$caller_ty}
+
+const_eval_incompatible_types =
+ calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}
+
const_eval_interior_mutability_borrow =
cannot borrow here, since the borrowed element may contain interior mutability
const_eval_interior_mutable_data_refer =
- {$kind}s cannot refer to interior mutable data
+ {const_eval_const_context}s cannot refer to interior mutable data
.label = this borrow of an interior mutable value may end up in the final value
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
.teach_note =
@@ -10,19 +144,166 @@ const_eval_interior_mutable_data_refer =
This would make multiple uses of a constant to be able to see different values and allow circumventing
the `Send` and `Sync` requirements for shared mutable data, which is unsound.
+const_eval_invalid_align =
+ align has to be a power of 2
+
+const_eval_invalid_align_details =
+ invalid align passed to `{$name}`: {$align} is {$err_kind ->
+ [not_power_of_two] not a power of 2
+ [too_large] too large
+ *[other] {""}
+ }
+
+const_eval_invalid_bool =
+ interpreting an invalid 8-bit value as a bool: 0x{$value}
+const_eval_invalid_box_meta = {$front_matter}: encountered invalid box metadata: total size is bigger than largest supported object
+const_eval_invalid_box_slice_meta = {$front_matter}: encountered invalid box metadata: slice is bigger than largest supported object
+const_eval_invalid_char =
+ interpreting an invalid 32-bit value as a char: 0x{$value}
+const_eval_invalid_dealloc =
+ deallocating {$alloc_id}, which is {$kind ->
+ [fn] a function
+ [vtable] a vtable
+ [static_mem] static memory
+ *[other] {""}
+ }
+
+const_eval_invalid_enum_tag = {$front_matter}: encountered {$value}, but expected a valid enum tag
+const_eval_invalid_fn_ptr = {$front_matter}: encountered {$value}, but expected a function pointer
+const_eval_invalid_function_pointer =
+ using {$pointer} as function pointer but it does not point to a function
+const_eval_invalid_meta =
+ invalid metadata in wide pointer: total size is bigger than largest supported object
+const_eval_invalid_meta_slice =
+ invalid metadata in wide pointer: slice is bigger than largest supported object
+const_eval_invalid_ref_meta = {$front_matter}: encountered invalid reference metadata: total size is bigger than largest supported object
+const_eval_invalid_ref_slice_meta = {$front_matter}: encountered invalid reference metadata: slice is bigger than largest supported object
+const_eval_invalid_str =
+ this string is not valid UTF-8: {$err}
+const_eval_invalid_tag =
+ enum value has invalid tag: {$tag}
+const_eval_invalid_transmute =
+ transmuting from {$src_bytes}-byte type to {$dest_bytes}-byte type: `{$src}` -> `{$dest}`
+
+const_eval_invalid_uninit_bytes =
+ reading memory at {$alloc}{$access}, but memory is uninitialized at {$uninit}, and this operation requires initialized memory
+const_eval_invalid_uninit_bytes_unknown =
+ using uninitialized data, but this operation requires initialized memory
+const_eval_invalid_value = constructing invalid value
+const_eval_invalid_value_with_path = constructing invalid value at {$path}
+## The `front_matter`s here refer to either `middle_invalid_value` or `middle_invalid_value_with_path`.
+
+const_eval_invalid_vtable_pointer =
+ using {$pointer} as vtable pointer but it does not point to a vtable
+
+const_eval_invalid_vtable_ptr = {$front_matter}: encountered {$value}, but expected a vtable pointer
+
+const_eval_live_drop =
+ destructor of `{$dropped_ty}` cannot be evaluated at compile-time
+ .label = the destructor for this type cannot be evaluated in {const_eval_const_context}s
+ .dropped_at_label = value is dropped here
+
+const_eval_long_running =
+ constant evaluation is taking a long time
+ .note = this lint makes sure the compiler doesn't get stuck due to infinite loops in const eval.
+ If your compilation actually takes a long time, you can safely allow the lint.
+ .label = the const evaluator is currently interpreting this expression
+ .help = the constant being evaluated
+
+const_eval_match_eq_non_const = cannot match on `{$ty}` in {const_eval_const_context}s
+ .note = `{$ty}` cannot be compared in compile-time, and therefore cannot be used in `match`es
+
const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
+const_eval_memory_access_test = memory access failed
+const_eval_memory_exhausted =
+ tried to allocate more memory than available to compiler
+const_eval_modified_global =
+ modifying a static's initial value from another static's initializer
+
const_eval_mut_deref =
- mutation through a reference is not allowed in {$kind}s
+ mutation through a reference is not allowed in {const_eval_const_context}s
+const_eval_mutable_ref_in_const = {$front_matter}: encountered mutable reference in a `const`
+const_eval_never_val = {$front_matter}: encountered a value of the never type `!`
const_eval_non_const_fmt_macro_call =
- cannot call non-const formatting macro in {$kind}s
+ cannot call non-const formatting macro in {const_eval_const_context}s
const_eval_non_const_fn_call =
- cannot call non-const fn `{$def_path_str}` in {$kind}s
+ cannot call non-const fn `{$def_path_str}` in {const_eval_const_context}s
+
+const_eval_non_const_impl =
+ impl defined here, but it is not `const`
+
+const_eval_noreturn_asm_returned =
+ returned from noreturn inline assembly
+
+const_eval_not_enough_caller_args =
+ calling a function with fewer arguments than it requires
+
+const_eval_null_box = {$front_matter}: encountered a null box
+const_eval_null_fn_ptr = {$front_matter}: encountered a null function pointer
+const_eval_null_ref = {$front_matter}: encountered a null reference
+const_eval_nullable_ptr_out_of_range = {$front_matter}: encountered a potentially null pointer, but expected something that cannot possibly fail to be {$in_range}
+const_eval_nullary_intrinsic_fail =
+ could not evaluate nullary intrinsic
+
+const_eval_offset_from_overflow =
+ `{$name}` called when first pointer is too far ahead of second
+
+const_eval_offset_from_test = out-of-bounds `offset_from`
+const_eval_offset_from_underflow =
+ `{$name}` called when first pointer is too far before second
+
+const_eval_operator_non_const =
+ cannot call non-const operator in {const_eval_const_context}s
+const_eval_out_of_range = {$front_matter}: encountered {$value}, but expected something {$in_range}
+const_eval_overflow =
+ overflow executing `{$name}`
+
+const_eval_overflow_shift =
+ overflowing shift by {$val} in `{$name}`
+
+const_eval_panic =
+ the evaluated program panicked at '{$msg}', {$file}:{$line}:{$col}
const_eval_panic_non_str = argument to `panic!()` in a const context must have type `&str`
+const_eval_partial_pointer_copy =
+ unable to copy parts of a pointer from memory at {$ptr}
+const_eval_partial_pointer_overwrite =
+ unable to overwrite parts of a pointer in memory at {$ptr}
+const_eval_pointer_arithmetic_overflow =
+ overflowing in-bounds pointer arithmetic
+const_eval_pointer_arithmetic_test = out-of-bounds pointer arithmetic
+const_eval_pointer_out_of_bounds =
+ {$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer to {$ptr_size} {$ptr_size ->
+ [1] byte
+ *[many] bytes
+ } starting at offset {$ptr_offset} is out-of-bounds
+const_eval_pointer_use_after_free =
+ pointer to {$allocation} was dereferenced after this allocation got freed
+const_eval_ptr_as_bytes_1 =
+ this code performed an operation that depends on the underlying bytes representing a pointer
+const_eval_ptr_as_bytes_2 =
+ the absolute address of a pointer is not known at compile-time, so such operations are not supported
+const_eval_ptr_out_of_range = {$front_matter}: encountered a pointer, but expected something that cannot possibly fail to be {$in_range}
+const_eval_question_branch_non_const =
+ `?` cannot determine the branch of `{$ty}` in {const_eval_const_context}s
+
+const_eval_question_from_residual_non_const =
+ `?` cannot convert from residual of `{$ty}` in {const_eval_const_context}s
+
+const_eval_range = in the range {$lo}..={$hi}
+const_eval_range_lower = greater or equal to {$lo}
+const_eval_range_singular = equal to {$lo}
+const_eval_range_upper = less or equal to {$hi}
+const_eval_range_wrapping = less or equal to {$hi}, or greater or equal to {$lo}
+const_eval_raw_bytes = the raw bytes of the constant (size: {$size}, align: {$align}) {"{"}{$bytes}{"}"}
+
+const_eval_raw_eq_with_provenance =
+ `raw_eq` on bytes with provenance
+
const_eval_raw_ptr_comparison =
pointers cannot be reliably compared during const eval
.note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information
@@ -32,8 +313,36 @@ const_eval_raw_ptr_to_int =
.note = at compile-time, pointers do not have an integer value
.note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior
+const_eval_read_extern_static =
+ cannot read from extern static ({$did})
+const_eval_read_pointer_as_bytes =
+ unable to turn pointer into raw bytes
+const_eval_realloc_or_alloc_with_offset =
+ {$kind ->
+ [dealloc] deallocating
+ [realloc] reallocating
+ *[other] {""}
+ } {$ptr} which does not point to the beginning of an object
+
+const_eval_ref_to_mut = {$front_matter}: encountered a reference pointing to mutable memory in a constant
+const_eval_ref_to_static = {$front_matter}: encountered a reference pointing to a static variable in a constant
+const_eval_ref_to_uninhabited = {$front_matter}: encountered a reference pointing to uninhabited type {$ty}
+const_eval_remainder_by_zero =
+ calculating the remainder with a divisor of zero
+const_eval_remainder_overflow =
+ overflow in signed remainder (dividing MIN by -1)
+const_eval_scalar_size_mismatch =
+ scalar size mismatch: expected {$target_size} bytes but got {$data_size} bytes instead
+const_eval_size_of_unsized =
+ size_of called on unsized type `{$ty}`
+const_eval_size_overflow =
+ overflow computing total size of `{$name}`
+
+const_eval_stack_frame_limit_reached =
+ reached the configured maximum number of stack frames
+
const_eval_static_access =
- {$kind}s cannot refer to statics
+ {const_eval_const_context}s cannot refer to statics
.help = consider extracting the value of the `static` to a `const`, and referring to that
.teach_note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.
.teach_help = To fix this, the value can be extracted to a `const` and then used.
@@ -41,27 +350,34 @@ const_eval_static_access =
const_eval_thread_local_access =
thread-local statics cannot be accessed at compile-time
-const_eval_transient_mut_borrow = mutable references are not allowed in {$kind}s
+const_eval_thread_local_static =
+ cannot access thread local static ({$did})
+const_eval_too_generic =
+ encountered overly generic constant
+const_eval_too_many_caller_args =
+ calling a function with more arguments than it expected
-const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {$kind}s
+const_eval_transient_mut_borrow = mutable references are not allowed in {const_eval_const_context}s
-const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {$kind}s
+const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {const_eval_const_context}s
+
+const_eval_try_block_from_output_non_const =
+ `try` block cannot convert `{$ty}` to the result in {const_eval_const_context}s
+const_eval_unaligned_box = {$front_matter}: encountered an unaligned box (required {$required_bytes} byte alignment but found {$found_bytes})
+const_eval_unaligned_ref = {$front_matter}: encountered an unaligned reference (required {$required_bytes} byte alignment but found {$found_bytes})
+const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {const_eval_const_context}s
const_eval_unallowed_heap_allocations =
- allocations are not allowed in {$kind}s
- .label = allocation not allowed in {$kind}s
+ allocations are not allowed in {const_eval_const_context}s
+ .label = allocation not allowed in {const_eval_const_context}s
.teach_note =
The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
const_eval_unallowed_inline_asm =
- inline assembly is not allowed in {$kind}s
-
+ inline assembly is not allowed in {const_eval_const_context}s
const_eval_unallowed_mutable_refs =
- mutable references are not allowed in the final value of {$kind}s
+ mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note =
- References in statics and constants may only refer to immutable values.
-
-
Statics are shared everywhere, and if they refer to mutable data one might violate memory
safety since holding multiple mutable references to shared data is not allowed.
@@ -69,7 +385,7 @@ const_eval_unallowed_mutable_refs =
If you really want global mutable state, try using static mut or a global UnsafeCell.
const_eval_unallowed_mutable_refs_raw =
- raw mutable references are not allowed in the final value of {$kind}s
+ raw mutable references are not allowed in the final value of {const_eval_const_context}s
.teach_note =
References in statics and constants may only refer to immutable values.
@@ -83,9 +399,59 @@ const_eval_unallowed_mutable_refs_raw =
const_eval_unallowed_op_in_const_context =
{$msg}
+const_eval_undefined_behavior =
+ it is undefined behavior to use this value
+
+const_eval_undefined_behavior_note =
+ The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+
+const_eval_uninhabited_enum_variant_written =
+ writing discriminant of an uninhabited enum
+const_eval_uninhabited_val = {$front_matter}: encountered a value of uninhabited type `{$ty}`
+const_eval_uninit = {$front_matter}: encountered uninitialized bytes
+const_eval_uninit_bool = {$front_matter}: encountered uninitialized memory, but expected a boolean
+const_eval_uninit_box = {$front_matter}: encountered uninitialized memory, but expected a box
+const_eval_uninit_char = {$front_matter}: encountered uninitialized memory, but expected a unicode scalar value
+const_eval_uninit_enum_tag = {$front_matter}: encountered uninitialized bytes, but expected a valid enum tag
+const_eval_uninit_float = {$front_matter}: encountered uninitialized memory, but expected a floating point number
+const_eval_uninit_fn_ptr = {$front_matter}: encountered uninitialized memory, but expected a function pointer
+const_eval_uninit_init_scalar = {$front_matter}: encountered uninitialized memory, but expected initialized scalar value
+const_eval_uninit_int = {$front_matter}: encountered uninitialized memory, but expected an integer
+const_eval_uninit_raw_ptr = {$front_matter}: encountered uninitialized memory, but expected a raw pointer
+const_eval_uninit_ref = {$front_matter}: encountered uninitialized memory, but expected a reference
+const_eval_uninit_str = {$front_matter}: encountered uninitialized data in `str`
+const_eval_uninit_unsized_local =
+ unsized local is used while uninitialized
+const_eval_unreachable = entering unreachable code
+const_eval_unreachable_unwind =
+ unwinding past a stack frame that does not allow unwinding
+
+const_eval_unsafe_cell = {$front_matter}: encountered `UnsafeCell` in a `const`
+const_eval_unsigned_offset_from_overflow =
+ `ptr_offset_from_unsigned` called when first pointer has smaller offset than second: {$a_offset} < {$b_offset}
+
const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn
const_eval_unstable_in_stable =
const-stable function cannot use `#[feature({$gate})]`
.unstable_sugg = if it is not part of the public API, make this function unstably const
.bypass_sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks
+
+const_eval_unsupported_untyped_pointer = unsupported untyped pointer in constant
+ .note = memory only reachable via raw pointers is not supported
+
+const_eval_unterminated_c_string =
+ reading a null-terminated string starting at {$pointer} with no null found before end of allocation
+
+const_eval_unwind_past_top =
+ unwinding past the topmost frame of the stack
+
+const_eval_upcast_mismatch =
+ upcast on a pointer whose vtable does not match its type
+
+const_eval_validation_invalid_bool = {$front_matter}: encountered {$value}, but expected a boolean
+const_eval_validation_invalid_char = {$front_matter}: encountered {$value}, but expected a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)
+const_eval_write_to_read_only =
+ writing to {$allocation} which is read-only
+const_eval_zst_pointer_out_of_bounds =
+ {$bad_pointer_message}: {$alloc_id} has size {$alloc_size}, so pointer at offset {$ptr_offset} is out-of-bounds
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index c591ff75a..7890d878d 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -1,17 +1,15 @@
-use std::error::Error;
-use std::fmt;
+use std::mem;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, IntoDiagnostic, IntoDiagnosticArg};
use rustc_middle::mir::AssertKind;
-use rustc_middle::query::TyCtxtAt;
+use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::{layout::LayoutError, ConstInt};
-use rustc_span::{Span, Symbol};
+use rustc_span::source_map::Spanned;
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
use super::InterpCx;
-use crate::interpret::{
- struct_error, ErrorHandled, FrameInfo, InterpError, InterpErrorInfo, Machine, MachineStopType,
- UnsupportedOpInfo,
-};
+use crate::errors::{self, FrameNote, ReportErrorExt};
+use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, Machine, MachineStopType};
/// The CTFE machine has some custom error kinds.
#[derive(Clone, Debug)]
@@ -23,7 +21,35 @@ pub enum ConstEvalErrKind {
Abort(String),
}
-impl MachineStopType for ConstEvalErrKind {}
+impl MachineStopType for ConstEvalErrKind {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ use ConstEvalErrKind::*;
+ match self {
+ ConstAccessesStatic => const_eval_const_accesses_static,
+ ModifiedGlobal => const_eval_modified_global,
+ Panic { .. } => const_eval_panic,
+ AssertFailure(x) => x.diagnostic_message(),
+ Abort(msg) => msg.to_string().into(),
+ }
+ }
+ fn add_args(
+ self: Box<Self>,
+ adder: &mut dyn FnMut(std::borrow::Cow<'static, str>, DiagnosticArgValue<'static>),
+ ) {
+ use ConstEvalErrKind::*;
+ match *self {
+ ConstAccessesStatic | ModifiedGlobal | Abort(_) => {}
+ AssertFailure(kind) => kind.add_args(adder),
+ Panic { msg, line, col, file } => {
+ adder("msg".into(), msg.into_diagnostic_arg());
+ adder("file".into(), file.into_diagnostic_arg());
+ adder("line".into(), line.into_diagnostic_arg());
+ adder("col".into(), col.into_diagnostic_arg());
+ }
+ }
+ }
+}
// The errors become `MachineStop` with plain strings when being raised.
// `ConstEvalErr` (in `librustc_middle/mir/interpret/error.rs`) knows to
@@ -34,151 +60,117 @@ impl<'tcx> Into<InterpErrorInfo<'tcx>> for ConstEvalErrKind {
}
}
-impl fmt::Display for ConstEvalErrKind {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use self::ConstEvalErrKind::*;
- match self {
- ConstAccessesStatic => write!(f, "constant accesses static"),
- ModifiedGlobal => {
- write!(f, "modifying a static's initial value from another static's initializer")
- }
- AssertFailure(msg) => write!(f, "{:?}", msg),
- Panic { msg, line, col, file } => {
- write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
- }
- Abort(msg) => write!(f, "{}", msg),
- }
- }
-}
-
-impl Error for ConstEvalErrKind {}
+pub fn get_span_and_frames<'tcx, 'mir, M: Machine<'mir, 'tcx>>(
+ ecx: &InterpCx<'mir, 'tcx, M>,
+) -> (Span, Vec<errors::FrameNote>)
+where
+ 'tcx: 'mir,
+{
+ let mut stacktrace = ecx.generate_stacktrace();
+ // Filter out `requires_caller_location` frames.
+ stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
+ let span = stacktrace.first().map(|f| f.span).unwrap_or(ecx.tcx.span);
-/// When const-evaluation errors, this type is constructed with the resulting information,
-/// and then used to emit the error as a lint or hard error.
-#[derive(Debug)]
-pub(super) struct ConstEvalErr<'tcx> {
- pub span: Span,
- pub error: InterpError<'tcx>,
- pub stacktrace: Vec<FrameInfo<'tcx>>,
-}
+ let mut frames = Vec::new();
-impl<'tcx> ConstEvalErr<'tcx> {
- /// Turn an interpreter error into something to report to the user.
- /// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
- /// Should be called only if the error is actually going to be reported!
- pub fn new<'mir, M: Machine<'mir, 'tcx>>(
- ecx: &InterpCx<'mir, 'tcx, M>,
- error: InterpErrorInfo<'tcx>,
- span: Option<Span>,
- ) -> ConstEvalErr<'tcx>
- where
- 'tcx: 'mir,
- {
- error.print_backtrace();
- let mut stacktrace = ecx.generate_stacktrace();
- // Filter out `requires_caller_location` frames.
- stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*ecx.tcx));
- // If `span` is missing, use topmost remaining frame, or else the "root" span from `ecx.tcx`.
- let span = span.or_else(|| stacktrace.first().map(|f| f.span)).unwrap_or(ecx.tcx.span);
- ConstEvalErr { error: error.into_kind(), stacktrace, span }
- }
-
- pub(super) fn report(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
- self.report_decorated(tcx, message, |_| {})
- }
-
- #[instrument(level = "trace", skip(self, decorate))]
- pub(super) fn decorate(&self, err: &mut Diagnostic, decorate: impl FnOnce(&mut Diagnostic)) {
- trace!("reporting const eval failure at {:?}", self.span);
- // Add some more context for select error types.
- match self.error {
- InterpError::Unsupported(
- UnsupportedOpInfo::ReadPointerAsBytes
- | UnsupportedOpInfo::PartialPointerOverwrite(_)
- | UnsupportedOpInfo::PartialPointerCopy(_),
- ) => {
- err.help("this code performed an operation that depends on the underlying bytes representing a pointer");
- err.help("the absolute address of a pointer is not known at compile-time, so such operations are not supported");
+ // Add notes to the backtrace. Don't print a single-line backtrace though.
+ if stacktrace.len() > 1 {
+ // Helper closure to print duplicated lines.
+ let mut add_frame = |mut frame: errors::FrameNote| {
+ frames.push(errors::FrameNote { times: 0, ..frame.clone() });
+ // Don't print [... additional calls ...] if the number of lines is small
+ if frame.times < 3 {
+ let times = frame.times;
+ frame.times = 0;
+ frames.extend(std::iter::repeat(frame).take(times as usize));
+ } else {
+ frames.push(frame);
}
- _ => {}
- }
- // Add spans for the stacktrace. Don't print a single-line backtrace though.
- if self.stacktrace.len() > 1 {
- // Helper closure to print duplicated lines.
- let mut flush_last_line = |last_frame: Option<(String, _)>, times| {
- if let Some((line, span)) = last_frame {
- err.span_note(span, line.clone());
- // Don't print [... additional calls ...] if the number of lines is small
- if times < 3 {
- for _ in 0..times {
- err.span_note(span, line.clone());
- }
- } else {
- err.span_note(
- span,
- format!("[... {} additional calls {} ...]", times, &line),
- );
- }
- }
- };
+ };
- let mut last_frame = None;
- let mut times = 0;
- for frame_info in &self.stacktrace {
- let frame = (frame_info.to_string(), frame_info.span);
- if last_frame.as_ref() == Some(&frame) {
- times += 1;
- } else {
- flush_last_line(last_frame, times);
+ let mut last_frame: Option<errors::FrameNote> = None;
+ for frame_info in &stacktrace {
+ let frame = frame_info.as_note(*ecx.tcx);
+ match last_frame.as_mut() {
+ Some(last_frame)
+ if last_frame.span == frame.span
+ && last_frame.where_ == frame.where_
+ && last_frame.instance == frame.instance =>
+ {
+ last_frame.times += 1;
+ }
+ Some(last_frame) => {
+ add_frame(mem::replace(last_frame, frame));
+ }
+ None => {
last_frame = Some(frame);
- times = 0;
}
}
- flush_last_line(last_frame, times);
}
- // Let the caller attach any additional information it wants.
- decorate(err);
+ if let Some(frame) = last_frame {
+ add_frame(frame);
+ }
}
- /// Create a diagnostic for this const eval error.
- ///
- /// Sets the message passed in via `message` and adds span labels with detailed error
- /// information before handing control back to `decorate` to do any final annotations,
- /// after which the diagnostic is emitted.
- ///
- /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
- /// (Except that for some errors, we ignore all that -- see `must_error` below.)
- #[instrument(skip(self, tcx, decorate), level = "debug")]
- pub(super) fn report_decorated(
- &self,
- tcx: TyCtxtAt<'tcx>,
- message: &str,
- decorate: impl FnOnce(&mut Diagnostic),
- ) -> ErrorHandled {
- debug!("self.error: {:?}", self.error);
- // Special handling for certain errors
- match &self.error {
- // Don't emit a new diagnostic for these errors
- err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
- ErrorHandled::TooGeneric
- }
- err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(*error_reported),
- err_inval!(Layout(LayoutError::SizeOverflow(_))) => {
- // We must *always* hard error on these, even if the caller wants just a lint.
- // The `message` makes little sense here, this is a more serious error than the
- // caller thinks anyway.
- // See <https://github.com/rust-lang/rust/pull/63152>.
- let mut err = struct_error(tcx, &self.error.to_string());
- self.decorate(&mut err, decorate);
- ErrorHandled::Reported(err.emit().into())
- }
- _ => {
- // Report as hard error.
- let mut err = struct_error(tcx, message);
- err.span_label(self.span, self.error.to_string());
- self.decorate(&mut err, decorate);
- ErrorHandled::Reported(err.emit().into())
+ (span, frames)
+}
+
+/// Create a diagnostic for a const eval error.
+///
+/// This will use the `mk` function for creating the error which will get passed labels according to
+/// the `InterpError` and the span and a stacktrace of current execution according to
+/// `get_span_and_frames`.
+pub(super) fn report<'tcx, C, F, E>(
+ tcx: TyCtxt<'tcx>,
+ error: InterpError<'tcx>,
+ span: Option<Span>,
+ get_span_and_frames: C,
+ mk: F,
+) -> ErrorHandled
+where
+ C: FnOnce() -> (Span, Vec<FrameNote>),
+ F: FnOnce(Span, Vec<FrameNote>) -> E,
+ E: IntoDiagnostic<'tcx, ErrorGuaranteed>,
+{
+ // Special handling for certain errors
+ match error {
+ // Don't emit a new diagnostic for these errors
+ err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => {
+ ErrorHandled::TooGeneric
+ }
+ err_inval!(AlreadyReported(error_reported)) => ErrorHandled::Reported(error_reported),
+ err_inval!(Layout(layout_error @ LayoutError::SizeOverflow(_))) => {
+ // We must *always* hard error on these, even if the caller wants just a lint.
+ // The `message` makes little sense here, this is a more serious error than the
+ // caller thinks anyway.
+ // See <https://github.com/rust-lang/rust/pull/63152>.
+ let (our_span, frames) = get_span_and_frames();
+ let span = span.unwrap_or(our_span);
+ let mut err =
+ tcx.sess.create_err(Spanned { span, node: layout_error.into_diagnostic() });
+ err.code(rustc_errors::error_code!(E0080));
+ let Some((mut err, handler)) = err.into_diagnostic() else {
+ panic!("did not emit diag");
+ };
+ for frame in frames {
+ err.eager_subdiagnostic(handler, frame);
}
+
+ ErrorHandled::Reported(handler.emit_diagnostic(&mut err).unwrap().into())
+ }
+ _ => {
+ // Report as hard error.
+ let (our_span, frames) = get_span_and_frames();
+ let span = span.unwrap_or(our_span);
+ let err = mk(span, frames);
+ let mut err = tcx.sess.create_err(err);
+
+ let msg = error.diagnostic_message();
+ error.add_args(&tcx.sess.parse_sess.span_diagnostic, &mut err);
+
+ // Use *our* span to label the interp error
+ err.span_label(our_span, msg);
+ ErrorHandled::Reported(err.emit().into())
}
}
}
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 046d20529..417ab78fd 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -1,12 +1,12 @@
use crate::const_eval::CheckAlignment;
-use std::borrow::Cow;
+use crate::errors::ConstEvalError;
use either::{Left, Right};
use rustc_hir::def::DefKind;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::ErrorHandled;
-use rustc_middle::mir::pretty::display_allocation;
+use rustc_middle::mir::interpret::{ErrorHandled, InterpErrorInfo};
+use rustc_middle::mir::pretty::write_allocation_bytes;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -14,7 +14,8 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_span::source_map::Span;
use rustc_target::abi::{self, Abi};
-use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr};
+use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
+use crate::errors;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
@@ -22,10 +23,6 @@ use crate::interpret::{
RefTracking, StackPopCleanup,
};
-const NOTE_ON_UNDEFINED_BEHAVIOR_ERROR: &str = "The rules on what exactly is undefined behavior aren't clear, \
- so this check might be overzealous. Please open an issue on the rustc \
- repository if you believe it should not be considered undefined behavior.";
-
// Returns a pointer to where the result lives
fn eval_body_using_ecx<'mir, 'tcx>(
ecx: &mut CompileTimeEvalContext<'mir, 'tcx>,
@@ -96,14 +93,14 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
tcx: TyCtxt<'tcx>,
root_span: Span,
param_env: ty::ParamEnv<'tcx>,
- can_access_statics: bool,
+ can_access_statics: CanAccessStatics,
) -> CompileTimeEvalContext<'mir, 'tcx> {
debug!("mk_eval_cx: {:?}", param_env);
InterpCx::new(
tcx,
root_span,
param_env,
- CompileTimeInterpreter::new(tcx.const_eval_limit(), can_access_statics, CheckAlignment::No),
+ CompileTimeInterpreter::new(can_access_statics, CheckAlignment::No),
)
}
@@ -210,7 +207,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
tcx,
tcx.def_span(key.value.instance.def_id()),
key.param_env,
- /*can_access_statics:*/ is_static,
+ CanAccessStatics::from(is_static),
);
let mplace = ecx.raw_const_to_mplace(constant).expect(
@@ -253,8 +250,14 @@ pub fn eval_to_const_value_raw_provider<'tcx>(
};
return eval_nullary_intrinsic(tcx, key.param_env, def_id, substs).map_err(|error| {
let span = tcx.def_span(def_id);
- let error = ConstEvalErr { error: error.into_kind(), stacktrace: vec![], span };
- error.report(tcx.at(span), "could not evaluate nullary intrinsic")
+
+ super::report(
+ tcx,
+ error.into_kind(),
+ Some(span),
+ || (span, vec![]),
+ |span, _| errors::NullaryIntrinsicError { span },
+ )
});
}
@@ -306,8 +309,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
// Statics (and promoteds inside statics) may access other statics, because unlike consts
// they do not have to behave "as if" they were evaluated at runtime.
CompileTimeInterpreter::new(
- tcx.const_eval_limit(),
- /*can_access_statics:*/ is_static,
+ CanAccessStatics::from(is_static),
if tcx.sess.opts.unstable_opts.extra_const_ub_checks {
CheckAlignment::Error
} else {
@@ -319,9 +321,11 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let res = ecx.load_mir(cid.instance.def, cid.promoted);
match res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, &body)) {
Err(error) => {
- let err = ConstEvalErr::new(&ecx, error, None);
- let msg = if is_static {
- Cow::from("could not evaluate static initializer")
+ let (error, backtrace) = error.into_parts();
+ backtrace.print_backtrace();
+
+ let (kind, instance) = if is_static {
+ ("static", String::new())
} else {
// If the current item has generics, we'd like to enrich the message with the
// instance and its substs: to show the actual compile-time values, in addition to
@@ -329,19 +333,29 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
let instance = &key.value.instance;
if !instance.substs.is_empty() {
let instance = with_no_trimmed_paths!(instance.to_string());
- let msg = format!("evaluation of `{}` failed", instance);
- Cow::from(msg)
+ ("const_with_path", instance)
} else {
- Cow::from("evaluation of constant value failed")
+ ("const", String::new())
}
};
- Err(err.report(ecx.tcx.at(err.span), &msg))
+ Err(super::report(
+ *ecx.tcx,
+ error,
+ None,
+ || super::get_span_and_frames(&ecx),
+ |span, frames| ConstEvalError {
+ span,
+ error_kind: kind,
+ instance,
+ frame_notes: frames,
+ },
+ ))
}
Ok(mplace) => {
// Since evaluation had no errors, validate the resulting constant.
// This is a separate `try` block to provide more targeted error reporting.
- let validation = try {
+ let validation: Result<_, InterpErrorInfo<'_>> = try {
let mut ref_tracking = RefTracking::new(mplace);
let mut inner = false;
while let Some((mplace, path)) = ref_tracking.todo.pop() {
@@ -358,23 +372,37 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
}
};
let alloc_id = mplace.ptr.provenance.unwrap();
+
+ // Validation failed, report an error. This is always a hard error.
if let Err(error) = validation {
- // Validation failed, report an error. This is always a hard error.
- let err = ConstEvalErr::new(&ecx, error, None);
- Err(err.report_decorated(
- ecx.tcx,
- "it is undefined behavior to use this value",
- |diag| {
- if matches!(err.error, InterpError::UndefinedBehavior(_)) {
- diag.note(NOTE_ON_UNDEFINED_BEHAVIOR_ERROR);
- }
- diag.note(format!(
- "the raw bytes of the constant ({}",
- display_allocation(
- *ecx.tcx,
- ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner()
- )
- ));
+ let (error, backtrace) = error.into_parts();
+ backtrace.print_backtrace();
+
+ let ub_note = matches!(error, InterpError::UndefinedBehavior(_)).then(|| {});
+
+ let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
+ let mut bytes = String::new();
+ if alloc.size() != abi::Size::ZERO {
+ bytes = "\n".into();
+ // FIXME(translation) there might be pieces that are translatable.
+ write_allocation_bytes(*ecx.tcx, alloc, &mut bytes, " ").unwrap();
+ }
+ let raw_bytes = errors::RawBytesNote {
+ size: alloc.size().bytes(),
+ align: alloc.align.bytes(),
+ bytes,
+ };
+
+ Err(super::report(
+ *ecx.tcx,
+ error,
+ None,
+ || super::get_span_and_frames(&ecx),
+ move |span, frames| errors::UndefinedBehavior {
+ span,
+ ub_note,
+ frames,
+ raw_bytes,
},
))
} else {
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 58b5755af..f9f645af4 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -16,25 +16,37 @@ use std::fmt;
use rustc_ast::Mutability;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::AssertMessage;
-use rustc_session::Limit;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::abi::{Align, Size};
use rustc_target::spec::abi::Abi as CallAbi;
+use crate::errors::{LongRunning, LongRunningWarn};
use crate::interpret::{
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
};
+use crate::{errors, fluent_generated as fluent};
use super::error::*;
+/// When hitting this many interpreted terminators we emit a deny by default lint
+/// that notfies the user that their constant takes a long time to evaluate. If that's
+/// what they intended, they can just allow the lint.
+const LINT_TERMINATOR_LIMIT: usize = 2_000_000;
+/// The limit used by `-Z tiny-const-eval-limit`. This smaller limit is useful for internal
+/// tests not needing to run 30s or more to show some behaviour.
+const TINY_LINT_TERMINATOR_LIMIT: usize = 20;
+/// After this many interpreted terminators, we start emitting progress indicators at every
+/// power of two of interpreted terminators.
+const PROGRESS_INDICATOR_START: usize = 4_000_000;
+
/// Extra machine state for CTFE, and the Machine instance
pub struct CompileTimeInterpreter<'mir, 'tcx> {
- /// For now, the number of terminators that can be evaluated before we throw a resource
- /// exhaustion error.
+ /// The number of terminators that have been evaluated.
///
- /// Setting this to `0` disables the limit and allows the interpreter to run forever.
- pub(super) steps_remaining: usize,
+ /// This is used to produce lints informing the user that the compiler is not stuck.
+ /// Set to `usize::MAX` to never report anything.
+ pub(super) num_evaluated_steps: usize,
/// The virtual call stack.
pub(super) stack: Vec<Frame<'mir, 'tcx, AllocId, ()>>,
@@ -45,7 +57,7 @@ pub struct CompileTimeInterpreter<'mir, 'tcx> {
/// * Interning makes everything outside of statics immutable.
/// * Pointers to allocations inside of statics can never leak outside, to a non-static global.
/// This boolean here controls the second part.
- pub(super) can_access_statics: bool,
+ pub(super) can_access_statics: CanAccessStatics,
/// Whether to check alignment during evaluation.
pub(super) check_alignment: CheckAlignment,
@@ -71,14 +83,25 @@ impl CheckAlignment {
}
}
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum CanAccessStatics {
+ No,
+ Yes,
+}
+
+impl From<bool> for CanAccessStatics {
+ fn from(value: bool) -> Self {
+ if value { Self::Yes } else { Self::No }
+ }
+}
+
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
pub(crate) fn new(
- const_eval_limit: Limit,
- can_access_statics: bool,
+ can_access_statics: CanAccessStatics,
check_alignment: CheckAlignment,
) -> Self {
CompileTimeInterpreter {
- steps_remaining: const_eval_limit.0,
+ num_evaluated_steps: 0,
stack: Vec::new(),
can_access_statics,
check_alignment,
@@ -247,7 +270,10 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
let target_align = self.read_scalar(&args[1])?.to_target_usize(self)?;
if !target_align.is_power_of_two() {
- throw_ub_format!("`align_offset` called with non-power-of-two align: {}", target_align);
+ throw_ub_custom!(
+ fluent::const_eval_align_offset_invalid_align,
+ target_align = target_align,
+ );
}
match self.ptr_try_get_alloc_id(ptr) {
@@ -353,15 +379,18 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
"`alignment_check_failed` called when no alignment check requested"
),
CheckAlignment::FutureIncompat => {
- let err = ConstEvalErr::new(ecx, err, None);
- ecx.tcx.struct_span_lint_hir(
+ let (_, backtrace) = err.into_parts();
+ backtrace.print_backtrace();
+ let (span, frames) = super::get_span_and_frames(&ecx);
+
+ ecx.tcx.emit_spanned_lint(
INVALID_ALIGNMENT,
ecx.stack().iter().find_map(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
- err.span,
- err.error.to_string(),
- |db| {
- err.decorate(db, |_| {});
- db
+ span,
+ errors::AlignmentCheckFailed {
+ has: has.bytes(),
+ required: required.bytes(),
+ frames,
},
);
Ok(())
@@ -475,7 +504,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let align = match Align::from_bytes(align) {
Ok(a) => a,
- Err(err) => throw_ub_format!("align has to be a power of 2, {}", err),
+ Err(err) => throw_ub_custom!(
+ fluent::const_eval_invalid_align_details,
+ name = "const_allocate",
+ err_kind = err.diag_ident(),
+ align = err.align()
+ ),
};
let ptr = ecx.allocate_ptr(
@@ -493,7 +527,12 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
let size = Size::from_bytes(size);
let align = match Align::from_bytes(align) {
Ok(a) => a,
- Err(err) => throw_ub_format!("align has to be a power of 2, {}", err),
+ Err(err) => throw_ub_custom!(
+ fluent::const_eval_invalid_align_details,
+ name = "const_deallocate",
+ err_kind = err.diag_ident(),
+ align = err.align()
+ ),
};
// If an allocation is created in an another const,
@@ -569,13 +608,54 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
// The step limit has already been hit in a previous call to `increment_const_eval_counter`.
- if ecx.machine.steps_remaining == 0 {
- return Ok(());
- }
- ecx.machine.steps_remaining -= 1;
- if ecx.machine.steps_remaining == 0 {
- throw_exhaust!(StepLimitReached)
+ if let Some(new_steps) = ecx.machine.num_evaluated_steps.checked_add(1) {
+ let (limit, start) = if ecx.tcx.sess.opts.unstable_opts.tiny_const_eval_limit {
+ (TINY_LINT_TERMINATOR_LIMIT, TINY_LINT_TERMINATOR_LIMIT)
+ } else {
+ (LINT_TERMINATOR_LIMIT, PROGRESS_INDICATOR_START)
+ };
+
+ ecx.machine.num_evaluated_steps = new_steps;
+ // By default, we have a *deny* lint kicking in after some time
+ // to ensure `loop {}` doesn't just go forever.
+ // In case that lint got reduced, in particular for `--cap-lint` situations, we also
+ // have a hard warning shown every now and then for really long executions.
+ if new_steps == limit {
+ // By default, we stop after a million steps, but the user can disable this lint
+ // to be able to run until the heat death of the universe or power loss, whichever
+ // comes first.
+ let hir_id = ecx.best_lint_scope();
+ let is_error = ecx
+ .tcx
+ .lint_level_at_node(
+ rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
+ hir_id,
+ )
+ .0
+ .is_error();
+ let span = ecx.cur_span();
+ ecx.tcx.emit_spanned_lint(
+ rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
+ hir_id,
+ span,
+ LongRunning { item_span: ecx.tcx.span },
+ );
+ // If this was a hard error, don't bother continuing evaluation.
+ if is_error {
+ let guard = ecx
+ .tcx
+ .sess
+ .delay_span_bug(span, "The deny lint should have already errored");
+ throw_inval!(AlreadyReported(guard.into()));
+ }
+ } else if new_steps > start && new_steps.is_power_of_two() {
+ // Only report after a certain number of terminators have been evaluated and the
+ // current number of evaluated terminators is a power of 2. The latter gives us a cheap
+ // way to implement exponential backoff.
+ let span = ecx.cur_span();
+ ecx.tcx.sess.emit_warning(LongRunningWarn { span, item_span: ecx.tcx.span });
+ }
}
Ok(())
@@ -634,7 +714,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
}
} else {
// Read access. These are usually allowed, with some exceptions.
- if machine.can_access_statics {
+ if machine.can_access_statics == CanAccessStatics::Yes {
// Machine configuration allows us read from anything (e.g., `static` initializer).
Ok(())
} else if static_def_id.is_some() {
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 05be45fef..a3064b53d 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,14 +1,10 @@
// Not in interpret to make sure we do not use private implementation details
use crate::errors::MaxNumNodesInConstErr;
-use crate::interpret::{
- intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
- Scalar,
-};
-use rustc_hir::Mutability;
+use crate::interpret::{intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, Scalar};
use rustc_middle::mir;
use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
mod error;
@@ -28,7 +24,7 @@ pub(crate) fn const_caller_location(
(file, line, col): (Symbol, u32, u32),
) -> ConstValue<'_> {
trace!("const_caller_location: {}:{}:{}", file, line, col);
- let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);
+ let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), CanAccessStatics::No);
let loc_place = ecx.alloc_caller_location(file, line, col);
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
@@ -57,10 +53,12 @@ pub(crate) fn eval_to_valtree<'tcx>(
// FIXME Need to provide a span to `eval_to_valtree`
let ecx = mk_eval_cx(
- tcx, DUMMY_SP, param_env,
+ tcx,
+ DUMMY_SP,
+ param_env,
// It is absolutely crucial for soundness that
// we do not read from static items or other mutable memory.
- false,
+ CanAccessStatics::No,
);
let place = ecx.raw_const_to_mplace(const_alloc).unwrap();
debug!(?place);
@@ -75,17 +73,8 @@ pub(crate) fn eval_to_valtree<'tcx>(
let global_const_id = cid.display(tcx);
match err {
ValTreeCreationError::NodesOverflow => {
- let msg = format!(
- "maximum number of nodes exceeded in constant {}",
- &global_const_id
- );
- let mut diag = match tcx.hir().span_if_local(did) {
- Some(span) => {
- tcx.sess.create_err(MaxNumNodesInConstErr { span, global_const_id })
- }
- None => tcx.sess.struct_err(msg),
- };
- diag.emit();
+ let span = tcx.hir().span_if_local(did);
+ tcx.sess.emit_err(MaxNumNodesInConstErr { span, global_const_id });
Ok(None)
}
@@ -96,24 +85,24 @@ pub(crate) fn eval_to_valtree<'tcx>(
}
#[instrument(skip(tcx), level = "debug")]
-pub(crate) fn try_destructure_mir_constant<'tcx>(
+pub(crate) fn try_destructure_mir_constant_for_diagnostics<'tcx>(
tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- val: mir::ConstantKind<'tcx>,
-) -> InterpResult<'tcx, mir::DestructuredConstant<'tcx>> {
- trace!("destructure_mir_constant: {:?}", val);
- let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
- let op = ecx.eval_mir_constant(&val, None, None)?;
+ val: ConstValue<'tcx>,
+ ty: Ty<'tcx>,
+) -> Option<mir::DestructuredConstant<'tcx>> {
+ let param_env = ty::ParamEnv::reveal_all();
+ let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
+ let op = ecx.const_val_to_op(val, ty, None).ok()?;
// We go to `usize` as we cannot allocate anything bigger anyway.
- let (field_count, variant, down) = match val.ty().kind() {
+ let (field_count, variant, down) = match ty.kind() {
ty::Array(_, len) => (len.eval_target_usize(tcx, param_env) as usize, None, op),
ty::Adt(def, _) if def.variants().is_empty() => {
- throw_ub!(Unreachable)
+ return None;
}
ty::Adt(def, _) => {
- let variant = ecx.read_discriminant(&op)?.1;
- let down = ecx.operand_downcast(&op, variant)?;
+ let variant = ecx.read_discriminant(&op).ok()?.1;
+ let down = ecx.operand_downcast(&op, variant).ok()?;
(def.variants()[variant].fields.len(), Some(variant), down)
}
ty::Tuple(substs) => (substs.len(), None, op),
@@ -122,47 +111,12 @@ pub(crate) fn try_destructure_mir_constant<'tcx>(
let fields_iter = (0..field_count)
.map(|i| {
- let field_op = ecx.operand_field(&down, i)?;
+ let field_op = ecx.operand_field(&down, i).ok()?;
let val = op_to_const(&ecx, &field_op);
- Ok(mir::ConstantKind::Val(val, field_op.layout.ty))
+ Some((val, field_op.layout.ty))
})
- .collect::<InterpResult<'tcx, Vec<_>>>()?;
+ .collect::<Option<Vec<_>>>()?;
let fields = tcx.arena.alloc_from_iter(fields_iter);
- Ok(mir::DestructuredConstant { variant, fields })
-}
-
-#[instrument(skip(tcx), level = "debug")]
-pub(crate) fn deref_mir_constant<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- val: mir::ConstantKind<'tcx>,
-) -> mir::ConstantKind<'tcx> {
- let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
- let op = ecx.eval_mir_constant(&val, None, None).unwrap();
- let mplace = ecx.deref_operand(&op).unwrap();
- if let Some(alloc_id) = mplace.ptr.provenance {
- assert_eq!(
- tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
- Mutability::Not,
- "deref_mir_constant cannot be used with mutable allocations as \
- that could allow pattern matching to observe mutable statics",
- );
- }
-
- let ty = match mplace.meta {
- MemPlaceMeta::None => mplace.layout.ty,
- // In case of unsized types, figure out the real type behind.
- MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
- ty::Str => bug!("there's no sized equivalent of a `str`"),
- ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_target_usize(&tcx).unwrap()),
- _ => bug!(
- "type {} should not have metadata, but had {:?}",
- mplace.layout.ty,
- mplace.meta
- ),
- },
- };
-
- mir::ConstantKind::Val(op_to_const(&ecx, &mplace.into()), ty)
+ Some(mir::DestructuredConstant { variant, fields })
}
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index b10f2e9f8..e574df276 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -1,6 +1,7 @@
use super::eval_queries::{mk_eval_cx, op_to_const};
use super::machine::CompileTimeEvalContext;
use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
+use crate::const_eval::CanAccessStatics;
use crate::interpret::{
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
MemoryKind, PlaceTy, Scalar,
@@ -263,7 +264,11 @@ pub fn valtree_to_const_value<'tcx>(
// FIXME Does this need an example?
let (param_env, ty) = param_env_ty.into_parts();
- let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+ let mut ecx: crate::interpret::InterpCx<
+ '_,
+ '_,
+ crate::const_eval::CompileTimeInterpreter<'_, '_>,
+ > = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessStatics::No);
match ty.kind() {
ty::FnDef(..) => {
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index f8b7cc6d7..ca38cce71 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -1,6 +1,24 @@
+use rustc_errors::{
+ DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
+ IntoDiagnostic,
+};
use rustc_hir::ConstContext;
-use rustc_macros::Diagnostic;
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
+use rustc_middle::mir::interpret::{
+ CheckInAllocMsg, ExpectedKind, InterpError, InvalidMetaKind, InvalidProgramInfo, PointerKind,
+ ResourceExhaustionInfo, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
+};
+use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
+use rustc_target::abi::call::AdjustForForeignAbiError;
+use rustc_target::abi::{Size, WrappingRange};
+
+#[derive(Diagnostic)]
+#[diag(const_eval_dangling_ptr_in_final)]
+pub(crate) struct DanglingPtrInFinal {
+ #[primary_span]
+ pub span: Span,
+}
#[derive(Diagnostic)]
#[diag(const_eval_unstable_in_stable)]
@@ -92,7 +110,7 @@ pub(crate) struct TransientMutBorrowErrRaw {
#[diag(const_eval_max_num_nodes_in_const)]
pub(crate) struct MaxNumNodesInConstErr {
#[primary_span]
- pub span: Span,
+ pub span: Option<Span>,
pub global_const_id: String,
}
@@ -176,6 +194,14 @@ pub(crate) struct UnallowedInlineAsm {
}
#[derive(Diagnostic)]
+#[diag(const_eval_unsupported_untyped_pointer)]
+#[note]
+pub(crate) struct UnsupportedUntypedPointer {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_data_refer, code = "E0492")]
pub(crate) struct InteriorMutableDataRefer {
#[primary_span]
@@ -194,3 +220,661 @@ pub(crate) struct InteriorMutabilityBorrow {
#[primary_span]
pub span: Span,
}
+
+#[derive(LintDiagnostic)]
+#[diag(const_eval_long_running)]
+#[note]
+pub struct LongRunning {
+ #[help]
+ pub item_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_long_running)]
+pub struct LongRunningWarn {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[help]
+ pub item_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_erroneous_constant)]
+pub(crate) struct ErroneousConstUsed {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[note(const_eval_non_const_impl)]
+pub(crate) struct NonConstImplNote {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Subdiagnostic, PartialEq, Eq, Clone)]
+#[note(const_eval_frame_note)]
+pub struct FrameNote {
+ #[primary_span]
+ pub span: Span,
+ pub times: i32,
+ pub where_: &'static str,
+ pub instance: String,
+}
+
+#[derive(Subdiagnostic)]
+#[note(const_eval_raw_bytes)]
+pub struct RawBytesNote {
+ pub size: u64,
+ pub align: u64,
+ pub bytes: String,
+}
+
+// FIXME(fee1-dead) do not use stringly typed `ConstContext`
+
+#[derive(Diagnostic)]
+#[diag(const_eval_match_eq_non_const, code = "E0015")]
+#[note]
+pub struct NonConstMatchEq<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_for_loop_into_iter_non_const, code = "E0015")]
+pub struct NonConstForLoopIntoIter<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_question_branch_non_const, code = "E0015")]
+pub struct NonConstQuestionBranch<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_question_from_residual_non_const, code = "E0015")]
+pub struct NonConstQuestionFromResidual<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_try_block_from_output_non_const, code = "E0015")]
+pub struct NonConstTryBlockFromOutput<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_await_non_const, code = "E0015")]
+pub struct NonConstAwait<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_closure_non_const, code = "E0015")]
+pub struct NonConstClosure {
+ #[primary_span]
+ pub span: Span,
+ pub kind: ConstContext,
+ #[subdiagnostic]
+ pub note: Option<NonConstClosureNote>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NonConstClosureNote {
+ #[note(const_eval_closure_fndef_not_const)]
+ FnDef {
+ #[primary_span]
+ span: Span,
+ },
+ #[note(const_eval_fn_ptr_call)]
+ FnPtr,
+ #[note(const_eval_closure_call)]
+ Closure,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(const_eval_consider_dereferencing, applicability = "machine-applicable")]
+pub struct ConsiderDereferencing {
+ pub deref: String,
+ #[suggestion_part(code = "{deref}")]
+ pub span: Span,
+ #[suggestion_part(code = "{deref}")]
+ pub rhs_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_operator_non_const, code = "E0015")]
+pub struct NonConstOperator {
+ #[primary_span]
+ pub span: Span,
+ pub kind: ConstContext,
+ #[subdiagnostic]
+ pub sugg: Option<ConsiderDereferencing>,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_deref_coercion_non_const, code = "E0015")]
+#[note]
+pub struct NonConstDerefCoercion<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+ pub kind: ConstContext,
+ pub target_ty: Ty<'tcx>,
+ #[note(const_eval_target_note)]
+ pub deref_target: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_live_drop, code = "E0493")]
+pub struct LiveDrop<'tcx> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub kind: ConstContext,
+ pub dropped_ty: Ty<'tcx>,
+ #[label(const_eval_dropped_at_label)]
+ pub dropped_at: Option<Span>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(const_eval_align_check_failed)]
+pub struct AlignmentCheckFailed {
+ pub has: u64,
+ pub required: u64,
+ #[subdiagnostic]
+ pub frames: Vec<FrameNote>,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_error, code = "E0080")]
+pub struct ConstEvalError {
+ #[primary_span]
+ pub span: Span,
+ /// One of "const", "const_with_path", and "static"
+ pub error_kind: &'static str,
+ pub instance: String,
+ #[subdiagnostic]
+ pub frame_notes: Vec<FrameNote>,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_nullary_intrinsic_fail)]
+pub struct NullaryIntrinsicError {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(const_eval_undefined_behavior, code = "E0080")]
+pub struct UndefinedBehavior {
+ #[primary_span]
+ pub span: Span,
+ #[note(const_eval_undefined_behavior_note)]
+ pub ub_note: Option<()>,
+ #[subdiagnostic]
+ pub frames: Vec<FrameNote>,
+ #[subdiagnostic]
+ pub raw_bytes: RawBytesNote,
+}
+
+pub trait ReportErrorExt {
+ /// Returns the diagnostic message for this error.
+ fn diagnostic_message(&self) -> DiagnosticMessage;
+ fn add_args<G: EmissionGuarantee>(
+ self,
+ handler: &Handler,
+ builder: &mut DiagnosticBuilder<'_, G>,
+ );
+
+ fn debug(self) -> String
+ where
+ Self: Sized,
+ {
+ ty::tls::with(move |tcx| {
+ let mut builder = tcx.sess.struct_allow(DiagnosticMessage::Str(String::new().into()));
+ let handler = &tcx.sess.parse_sess.span_diagnostic;
+ let message = self.diagnostic_message();
+ self.add_args(handler, &mut builder);
+ let s = handler.eagerly_translate_to_string(message, builder.args());
+ builder.cancel();
+ s
+ })
+ }
+}
+
+fn bad_pointer_message(msg: CheckInAllocMsg, handler: &Handler) -> String {
+ use crate::fluent_generated::*;
+
+ let msg = match msg {
+ CheckInAllocMsg::DerefTest => const_eval_deref_test,
+ CheckInAllocMsg::MemoryAccessTest => const_eval_memory_access_test,
+ CheckInAllocMsg::PointerArithmeticTest => const_eval_pointer_arithmetic_test,
+ CheckInAllocMsg::OffsetFromTest => const_eval_offset_from_test,
+ CheckInAllocMsg::InboundsTest => const_eval_in_bounds_test,
+ };
+
+ handler.eagerly_translate_to_string(msg, [].into_iter())
+}
+
+impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ use UndefinedBehaviorInfo::*;
+ match self {
+ Ub(msg) => msg.clone().into(),
+ Unreachable => const_eval_unreachable,
+ BoundsCheckFailed { .. } => const_eval_bounds_check_failed,
+ DivisionByZero => const_eval_division_by_zero,
+ RemainderByZero => const_eval_remainder_by_zero,
+ DivisionOverflow => const_eval_division_overflow,
+ RemainderOverflow => const_eval_remainder_overflow,
+ PointerArithOverflow => const_eval_pointer_arithmetic_overflow,
+ InvalidMeta(InvalidMetaKind::SliceTooBig) => const_eval_invalid_meta_slice,
+ InvalidMeta(InvalidMetaKind::TooBig) => const_eval_invalid_meta,
+ UnterminatedCString(_) => const_eval_unterminated_c_string,
+ PointerUseAfterFree(_) => const_eval_pointer_use_after_free,
+ PointerOutOfBounds { ptr_size: Size::ZERO, .. } => const_eval_zst_pointer_out_of_bounds,
+ PointerOutOfBounds { .. } => const_eval_pointer_out_of_bounds,
+ DanglingIntPointer(0, _) => const_eval_dangling_null_pointer,
+ DanglingIntPointer(_, _) => const_eval_dangling_int_pointer,
+ AlignmentCheckFailed { .. } => const_eval_alignment_check_failed,
+ WriteToReadOnly(_) => const_eval_write_to_read_only,
+ DerefFunctionPointer(_) => const_eval_deref_function_pointer,
+ DerefVTablePointer(_) => const_eval_deref_vtable_pointer,
+ InvalidBool(_) => const_eval_invalid_bool,
+ InvalidChar(_) => const_eval_invalid_char,
+ InvalidTag(_) => const_eval_invalid_tag,
+ InvalidFunctionPointer(_) => const_eval_invalid_function_pointer,
+ InvalidVTablePointer(_) => const_eval_invalid_vtable_pointer,
+ InvalidStr(_) => const_eval_invalid_str,
+ InvalidUninitBytes(None) => const_eval_invalid_uninit_bytes_unknown,
+ InvalidUninitBytes(Some(_)) => const_eval_invalid_uninit_bytes,
+ DeadLocal => const_eval_dead_local,
+ ScalarSizeMismatch(_) => const_eval_scalar_size_mismatch,
+ UninhabitedEnumVariantWritten => const_eval_uninhabited_enum_variant_written,
+ Validation(e) => e.diagnostic_message(),
+ Custom(x) => (x.msg)(),
+ }
+ }
+
+ fn add_args<G: EmissionGuarantee>(
+ self,
+ handler: &Handler,
+ builder: &mut DiagnosticBuilder<'_, G>,
+ ) {
+ use UndefinedBehaviorInfo::*;
+ match self {
+ Ub(_)
+ | Unreachable
+ | DivisionByZero
+ | RemainderByZero
+ | DivisionOverflow
+ | RemainderOverflow
+ | PointerArithOverflow
+ | InvalidMeta(InvalidMetaKind::SliceTooBig)
+ | InvalidMeta(InvalidMetaKind::TooBig)
+ | InvalidUninitBytes(None)
+ | DeadLocal
+ | UninhabitedEnumVariantWritten => {}
+ BoundsCheckFailed { len, index } => {
+ builder.set_arg("len", len);
+ builder.set_arg("index", index);
+ }
+ UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => {
+ builder.set_arg("pointer", ptr);
+ }
+ PointerUseAfterFree(allocation) => {
+ builder.set_arg("allocation", allocation);
+ }
+ PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => {
+ builder
+ .set_arg("alloc_id", alloc_id)
+ .set_arg("alloc_size", alloc_size.bytes())
+ .set_arg("ptr_offset", ptr_offset)
+ .set_arg("ptr_size", ptr_size.bytes())
+ .set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
+ }
+ DanglingIntPointer(ptr, msg) => {
+ if ptr != 0 {
+ builder.set_arg("pointer", format!("{ptr:#x}[noalloc]"));
+ }
+
+ builder.set_arg("bad_pointer_message", bad_pointer_message(msg, handler));
+ }
+ AlignmentCheckFailed { required, has } => {
+ builder.set_arg("required", required.bytes());
+ builder.set_arg("has", has.bytes());
+ }
+ WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
+ builder.set_arg("allocation", alloc);
+ }
+ InvalidBool(b) => {
+ builder.set_arg("value", format!("{b:02x}"));
+ }
+ InvalidChar(c) => {
+ builder.set_arg("value", format!("{c:08x}"));
+ }
+ InvalidTag(tag) => {
+ builder.set_arg("tag", format!("{tag:x}"));
+ }
+ InvalidStr(err) => {
+ builder.set_arg("err", format!("{err}"));
+ }
+ InvalidUninitBytes(Some((alloc, info))) => {
+ builder.set_arg("alloc", alloc);
+ builder.set_arg("access", info.access);
+ builder.set_arg("uninit", info.uninit);
+ }
+ ScalarSizeMismatch(info) => {
+ builder.set_arg("target_size", info.target_size);
+ builder.set_arg("data_size", info.data_size);
+ }
+ Validation(e) => e.add_args(handler, builder),
+ Custom(custom) => {
+ (custom.add_args)(&mut |name, value| {
+ builder.set_arg(name, value);
+ });
+ }
+ }
+ }
+}
+
+impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ use rustc_middle::mir::interpret::ValidationErrorKind::*;
+ match self.kind {
+ PtrToUninhabited { ptr_kind: PointerKind::Box, .. } => const_eval_box_to_uninhabited,
+ PtrToUninhabited { ptr_kind: PointerKind::Ref, .. } => const_eval_ref_to_uninhabited,
+
+ PtrToStatic { ptr_kind: PointerKind::Box } => const_eval_box_to_static,
+ PtrToStatic { ptr_kind: PointerKind::Ref } => const_eval_ref_to_static,
+
+ PtrToMut { ptr_kind: PointerKind::Box } => const_eval_box_to_mut,
+ PtrToMut { ptr_kind: PointerKind::Ref } => const_eval_ref_to_mut,
+
+ ExpectedNonPtr { .. } => const_eval_expected_non_ptr,
+ MutableRefInConst => const_eval_mutable_ref_in_const,
+ NullFnPtr => const_eval_null_fn_ptr,
+ NeverVal => const_eval_never_val,
+ NullablePtrOutOfRange { .. } => const_eval_nullable_ptr_out_of_range,
+ PtrOutOfRange { .. } => const_eval_ptr_out_of_range,
+ OutOfRange { .. } => const_eval_out_of_range,
+ UnsafeCell => const_eval_unsafe_cell,
+ UninhabitedVal { .. } => const_eval_uninhabited_val,
+ InvalidEnumTag { .. } => const_eval_invalid_enum_tag,
+ UninitEnumTag => const_eval_uninit_enum_tag,
+ UninitStr => const_eval_uninit_str,
+ Uninit { expected: ExpectedKind::Bool } => const_eval_uninit_bool,
+ Uninit { expected: ExpectedKind::Reference } => const_eval_uninit_ref,
+ Uninit { expected: ExpectedKind::Box } => const_eval_uninit_box,
+ Uninit { expected: ExpectedKind::RawPtr } => const_eval_uninit_raw_ptr,
+ Uninit { expected: ExpectedKind::InitScalar } => const_eval_uninit_init_scalar,
+ Uninit { expected: ExpectedKind::Char } => const_eval_uninit_char,
+ Uninit { expected: ExpectedKind::Float } => const_eval_uninit_float,
+ Uninit { expected: ExpectedKind::Int } => const_eval_uninit_int,
+ Uninit { expected: ExpectedKind::FnPtr } => const_eval_uninit_fn_ptr,
+ UninitVal => const_eval_uninit,
+ InvalidVTablePtr { .. } => const_eval_invalid_vtable_ptr,
+ InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Box } => {
+ const_eval_invalid_box_slice_meta
+ }
+ InvalidMetaSliceTooLarge { ptr_kind: PointerKind::Ref } => {
+ const_eval_invalid_ref_slice_meta
+ }
+
+ InvalidMetaTooLarge { ptr_kind: PointerKind::Box } => const_eval_invalid_box_meta,
+ InvalidMetaTooLarge { ptr_kind: PointerKind::Ref } => const_eval_invalid_ref_meta,
+ UnalignedPtr { ptr_kind: PointerKind::Ref, .. } => const_eval_unaligned_ref,
+ UnalignedPtr { ptr_kind: PointerKind::Box, .. } => const_eval_unaligned_box,
+
+ NullPtr { ptr_kind: PointerKind::Box } => const_eval_null_box,
+ NullPtr { ptr_kind: PointerKind::Ref } => const_eval_null_ref,
+ DanglingPtrNoProvenance { ptr_kind: PointerKind::Box, .. } => {
+ const_eval_dangling_box_no_provenance
+ }
+ DanglingPtrNoProvenance { ptr_kind: PointerKind::Ref, .. } => {
+ const_eval_dangling_ref_no_provenance
+ }
+ DanglingPtrOutOfBounds { ptr_kind: PointerKind::Box } => {
+ const_eval_dangling_box_out_of_bounds
+ }
+ DanglingPtrOutOfBounds { ptr_kind: PointerKind::Ref } => {
+ const_eval_dangling_ref_out_of_bounds
+ }
+ DanglingPtrUseAfterFree { ptr_kind: PointerKind::Box } => {
+ const_eval_dangling_box_use_after_free
+ }
+ DanglingPtrUseAfterFree { ptr_kind: PointerKind::Ref } => {
+ const_eval_dangling_ref_use_after_free
+ }
+ InvalidBool { .. } => const_eval_validation_invalid_bool,
+ InvalidChar { .. } => const_eval_validation_invalid_char,
+ InvalidFnPtr { .. } => const_eval_invalid_fn_ptr,
+ }
+ }
+
+ fn add_args<G: EmissionGuarantee>(self, handler: &Handler, err: &mut DiagnosticBuilder<'_, G>) {
+ use crate::fluent_generated as fluent;
+ use rustc_middle::mir::interpret::ValidationErrorKind::*;
+
+ let message = if let Some(path) = self.path {
+ handler.eagerly_translate_to_string(
+ fluent::const_eval_invalid_value_with_path,
+ [("path".into(), DiagnosticArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)),
+ )
+ } else {
+ handler.eagerly_translate_to_string(fluent::const_eval_invalid_value, [].into_iter())
+ };
+
+ err.set_arg("front_matter", message);
+
+ fn add_range_arg<G: EmissionGuarantee>(
+ r: WrappingRange,
+ max_hi: u128,
+ handler: &Handler,
+ err: &mut DiagnosticBuilder<'_, G>,
+ ) {
+ let WrappingRange { start: lo, end: hi } = r;
+ assert!(hi <= max_hi);
+ let msg = if lo > hi {
+ fluent::const_eval_range_wrapping
+ } else if lo == hi {
+ fluent::const_eval_range_singular
+ } else if lo == 0 {
+ assert!(hi < max_hi, "should not be printing if the range covers everything");
+ fluent::const_eval_range_upper
+ } else if hi == max_hi {
+ assert!(lo > 0, "should not be printing if the range covers everything");
+ fluent::const_eval_range_lower
+ } else {
+ fluent::const_eval_range
+ };
+
+ let args = [
+ ("lo".into(), DiagnosticArgValue::Str(lo.to_string().into())),
+ ("hi".into(), DiagnosticArgValue::Str(hi.to_string().into())),
+ ];
+ let args = args.iter().map(|(a, b)| (a, b));
+ let message = handler.eagerly_translate_to_string(msg, args);
+ err.set_arg("in_range", message);
+ }
+
+ match self.kind {
+ PtrToUninhabited { ty, .. } | UninhabitedVal { ty } => {
+ err.set_arg("ty", ty);
+ }
+ ExpectedNonPtr { value }
+ | InvalidEnumTag { value }
+ | InvalidVTablePtr { value }
+ | InvalidBool { value }
+ | InvalidChar { value }
+ | InvalidFnPtr { value } => {
+ err.set_arg("value", value);
+ }
+ NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
+ add_range_arg(range, max_value, handler, err)
+ }
+ OutOfRange { range, max_value, value } => {
+ err.set_arg("value", value);
+ add_range_arg(range, max_value, handler, err);
+ }
+ UnalignedPtr { required_bytes, found_bytes, .. } => {
+ err.set_arg("required_bytes", required_bytes);
+ err.set_arg("found_bytes", found_bytes);
+ }
+ DanglingPtrNoProvenance { pointer, .. } => {
+ err.set_arg("pointer", pointer);
+ }
+ NullPtr { .. }
+ | PtrToStatic { .. }
+ | PtrToMut { .. }
+ | MutableRefInConst
+ | NullFnPtr
+ | NeverVal
+ | UnsafeCell
+ | UninitEnumTag
+ | UninitStr
+ | Uninit { .. }
+ | UninitVal
+ | InvalidMetaSliceTooLarge { .. }
+ | InvalidMetaTooLarge { .. }
+ | DanglingPtrUseAfterFree { .. }
+ | DanglingPtrOutOfBounds { .. } => {}
+ }
+ }
+}
+
+impl ReportErrorExt for UnsupportedOpInfo {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ match self {
+ UnsupportedOpInfo::Unsupported(s) => s.clone().into(),
+ UnsupportedOpInfo::PartialPointerOverwrite(_) => const_eval_partial_pointer_overwrite,
+ UnsupportedOpInfo::PartialPointerCopy(_) => const_eval_partial_pointer_copy,
+ UnsupportedOpInfo::ReadPointerAsBytes => const_eval_read_pointer_as_bytes,
+ UnsupportedOpInfo::ThreadLocalStatic(_) => const_eval_thread_local_static,
+ UnsupportedOpInfo::ReadExternStatic(_) => const_eval_read_extern_static,
+ }
+ }
+ fn add_args<G: EmissionGuarantee>(self, _: &Handler, builder: &mut DiagnosticBuilder<'_, G>) {
+ use crate::fluent_generated::*;
+
+ use UnsupportedOpInfo::*;
+ if let ReadPointerAsBytes | PartialPointerOverwrite(_) | PartialPointerCopy(_) = self {
+ builder.help(const_eval_ptr_as_bytes_1);
+ builder.help(const_eval_ptr_as_bytes_2);
+ }
+ match self {
+ Unsupported(_) | ReadPointerAsBytes => {}
+ PartialPointerOverwrite(ptr) | PartialPointerCopy(ptr) => {
+ builder.set_arg("ptr", ptr);
+ }
+ ThreadLocalStatic(did) | ReadExternStatic(did) => {
+ builder.set_arg("did", format!("{did:?}"));
+ }
+ }
+ }
+}
+
+impl<'tcx> ReportErrorExt for InterpError<'tcx> {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ match self {
+ InterpError::UndefinedBehavior(ub) => ub.diagnostic_message(),
+ InterpError::Unsupported(e) => e.diagnostic_message(),
+ InterpError::InvalidProgram(e) => e.diagnostic_message(),
+ InterpError::ResourceExhaustion(e) => e.diagnostic_message(),
+ InterpError::MachineStop(e) => e.diagnostic_message(),
+ }
+ }
+ fn add_args<G: EmissionGuarantee>(
+ self,
+ handler: &Handler,
+ builder: &mut DiagnosticBuilder<'_, G>,
+ ) {
+ match self {
+ InterpError::UndefinedBehavior(ub) => ub.add_args(handler, builder),
+ InterpError::Unsupported(e) => e.add_args(handler, builder),
+ InterpError::InvalidProgram(e) => e.add_args(handler, builder),
+ InterpError::ResourceExhaustion(e) => e.add_args(handler, builder),
+ InterpError::MachineStop(e) => e.add_args(&mut |name, value| {
+ builder.set_arg(name, value);
+ }),
+ }
+ }
+}
+
+impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ match self {
+ InvalidProgramInfo::TooGeneric => const_eval_too_generic,
+ InvalidProgramInfo::AlreadyReported(_) => const_eval_already_reported,
+ InvalidProgramInfo::Layout(e) => e.diagnostic_message(),
+ InvalidProgramInfo::FnAbiAdjustForForeignAbi(_) => {
+ rustc_middle::error::middle_adjust_for_foreign_abi_error
+ }
+ InvalidProgramInfo::SizeOfUnsizedType(_) => const_eval_size_of_unsized,
+ InvalidProgramInfo::UninitUnsizedLocal => const_eval_uninit_unsized_local,
+ }
+ }
+ fn add_args<G: EmissionGuarantee>(
+ self,
+ handler: &Handler,
+ builder: &mut DiagnosticBuilder<'_, G>,
+ ) {
+ match self {
+ InvalidProgramInfo::TooGeneric
+ | InvalidProgramInfo::AlreadyReported(_)
+ | InvalidProgramInfo::UninitUnsizedLocal => {}
+ InvalidProgramInfo::Layout(e) => {
+ let diag: DiagnosticBuilder<'_, ()> = e.into_diagnostic().into_diagnostic(handler);
+ for (name, val) in diag.args() {
+ builder.set_arg(name.clone(), val.clone());
+ }
+ diag.cancel();
+ }
+ InvalidProgramInfo::FnAbiAdjustForForeignAbi(
+ AdjustForForeignAbiError::Unsupported { arch, abi },
+ ) => {
+ builder.set_arg("arch", arch);
+ builder.set_arg("abi", abi.name());
+ }
+ InvalidProgramInfo::SizeOfUnsizedType(ty) => {
+ builder.set_arg("ty", ty);
+ }
+ }
+ }
+}
+
+impl ReportErrorExt for ResourceExhaustionInfo {
+ fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ match self {
+ ResourceExhaustionInfo::StackFrameLimitReached => const_eval_stack_frame_limit_reached,
+ ResourceExhaustionInfo::MemoryExhausted => const_eval_memory_exhausted,
+ ResourceExhaustionInfo::AddressSpaceFull => const_eval_address_space_full,
+ }
+ }
+ fn add_args<G: EmissionGuarantee>(self, _: &Handler, _: &mut DiagnosticBuilder<'_, G>) {}
+}
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index 163e3f869..83a072d6f 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -4,7 +4,7 @@ use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
use rustc_middle::mir::CastKind;
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
use rustc_target::abi::Integer;
@@ -14,6 +14,8 @@ use super::{
util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy,
};
+use crate::fluent_generated as fluent;
+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn cast(
&mut self,
@@ -22,51 +24,52 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
cast_ty: Ty<'tcx>,
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
- use rustc_middle::mir::CastKind::*;
// FIXME: In which cases should we trigger UB when the source is uninit?
match cast_kind {
- Pointer(PointerCast::Unsize) => {
+ CastKind::PointerCoercion(PointerCoercion::Unsize) => {
let cast_ty = self.layout_of(cast_ty)?;
self.unsize_into(src, cast_ty, dest)?;
}
- PointerExposeAddress => {
+ CastKind::PointerExposeAddress => {
let src = self.read_immediate(src)?;
let res = self.pointer_expose_address_cast(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- PointerFromExposedAddress => {
+ CastKind::PointerFromExposedAddress => {
let src = self.read_immediate(src)?;
let res = self.pointer_from_exposed_address_cast(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- IntToInt | IntToFloat => {
+ CastKind::IntToInt | CastKind::IntToFloat => {
let src = self.read_immediate(src)?;
let res = self.int_to_int_or_float(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- FloatToFloat | FloatToInt => {
+ CastKind::FloatToFloat | CastKind::FloatToInt => {
let src = self.read_immediate(src)?;
let res = self.float_to_float_or_int(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- FnPtrToPtr | PtrToPtr => {
+ CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
let src = self.read_immediate(&src)?;
let res = self.ptr_to_ptr(&src, cast_ty)?;
self.write_immediate(res, dest)?;
}
- Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer) => {
+ CastKind::PointerCoercion(
+ PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer,
+ ) => {
// These are NOPs, but can be wide pointers.
let v = self.read_immediate(src)?;
self.write_immediate(*v, dest)?;
}
- Pointer(PointerCast::ReifyFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
// All reifications must be monomorphic, bail out otherwise.
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -88,7 +91,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Pointer(PointerCast::UnsafeFnPointer) => {
+ CastKind::PointerCoercion(PointerCoercion::UnsafeFnPointer) => {
let src = self.read_immediate(src)?;
match cast_ty.kind() {
ty::FnPtr(_) => {
@@ -99,7 +102,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Pointer(PointerCast::ClosureFnPointer(_)) => {
+ CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)) => {
// All reifications must be monomorphic, bail out otherwise.
ensure_monomorphic_enough(*self.tcx, src.layout.ty)?;
@@ -120,7 +123,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- DynStar => {
+ CastKind::DynStar => {
if let ty::Dynamic(data, _, ty::DynStar) = cast_ty.kind() {
// Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src.layout.ty, data.principal())?;
@@ -134,16 +137,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- Transmute => {
+ CastKind::Transmute => {
assert!(src.layout.is_sized());
assert!(dest.layout.is_sized());
if src.layout.size != dest.layout.size {
- throw_ub_format!(
- "transmuting from {}-byte type to {}-byte type: `{}` -> `{}`",
- src.layout.size.bytes(),
- dest.layout.size.bytes(),
- src.layout.ty,
- dest.layout.ty,
+ let src_bytes = src.layout.size.bytes();
+ let dest_bytes = dest.layout.size.bytes();
+ let src_ty = format!("{}", src.layout.ty);
+ let dest_ty = format!("{}", dest.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_invalid_transmute,
+ src_bytes = src_bytes,
+ dest_bytes = dest_bytes,
+ src = src_ty,
+ dest = dest_ty,
);
}
@@ -363,7 +370,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let old_vptr = old_vptr.to_pointer(self)?;
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
if old_trait != data_a.principal() {
- throw_ub_format!("upcast on a pointer whose vtable does not match its type");
+ throw_ub_custom!(fluent::const_eval_upcast_mismatch);
}
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 7e9457800..36606ff69 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -1,13 +1,13 @@
use std::cell::Cell;
-use std::fmt;
-use std::mem;
+use std::{fmt, mem};
use either::{Either, Left, Right};
+use hir::CRATE_HIR_ID;
use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::IndexVec;
use rustc_middle::mir;
-use rustc_middle::mir::interpret::{ErrorHandled, InterpError, ReportedErrorInfo};
+use rustc_middle::mir::interpret::{ErrorHandled, InterpError, InvalidMetaKind, ReportedErrorInfo};
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{
self, FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOf, LayoutOfHelpers,
@@ -24,6 +24,8 @@ use super::{
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
Scalar, StackPopJump,
};
+use crate::errors::{self, ErroneousConstUsed};
+use crate::fluent_generated as fluent;
use crate::util;
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
@@ -246,6 +248,7 @@ impl<'mir, 'tcx, Prov: Provenance, Extra> Frame<'mir, 'tcx, Prov, Extra> {
}
}
+// FIXME: only used by miri, should be removed once translatable.
impl<'tcx> fmt::Display for FrameInfo<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| {
@@ -263,6 +266,21 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
}
}
+impl<'tcx> FrameInfo<'tcx> {
+ pub fn as_note(&self, tcx: TyCtxt<'tcx>) -> errors::FrameNote {
+ let span = self.span;
+ if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
+ errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
+ } else {
+ let instance = format!("{}", self.instance);
+ // Note: this triggers a `good_path_bug` state, which means that if we ever get here
+ // we must emit a diagnostic. We should never display a `FrameInfo` unless we
+ // actually want to emit a warning or error to the user.
+ errors::FrameNote { where_: "instance", span, instance, times: 0 }
+ }
+ }
+}
+
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
@@ -406,6 +424,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
#[inline(always)]
+ /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId
+ pub fn best_lint_scope(&self) -> hir::HirId {
+ self.stack()
+ .iter()
+ .find_map(|frame| frame.body.source.def_id().as_local())
+ .map_or(CRATE_HIR_ID, |def_id| self.tcx.hir().local_def_id_to_hir_id(def_id))
+ }
+
+ #[inline(always)]
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>] {
M::stack(self)
}
@@ -497,7 +524,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
.try_subst_mir_and_normalize_erasing_regions(
*self.tcx,
self.param_env,
- ty::EarlyBinder(value),
+ ty::EarlyBinder::bind(value),
)
.map_err(|_| err_inval!(TooGeneric))
}
@@ -610,7 +637,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Check if this brought us over the size limit.
if size > self.max_size_of_val() {
- throw_ub!(InvalidMeta("total size is bigger than largest supported object"));
+ throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
}
Ok(Some((size, align)))
}
@@ -628,7 +655,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let size = elem.size.bytes().saturating_mul(len); // we rely on `max_size_of_val` being smaller than `u64::MAX`.
let size = Size::from_bytes(size);
if size > self.max_size_of_val() {
- throw_ub!(InvalidMeta("slice is bigger than largest supported object"));
+ throw_ub!(InvalidMeta(InvalidMetaKind::SliceTooBig));
}
Ok(Some((size, elem.align.abi)))
}
@@ -736,7 +763,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
mir::UnwindAction::Unreachable => {
- throw_ub_format!("unwinding past a stack frame that does not allow unwinding")
+ throw_ub_custom!(fluent::const_eval_unreachable_unwind);
}
mir::UnwindAction::Terminate => {
self.frame_mut().loc = Right(self.frame_mut().body.span);
@@ -775,7 +802,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
);
if unwinding && self.frame_idx() == 0 {
- throw_ub_format!("unwinding past the topmost frame of the stack");
+ throw_ub_custom!(fluent::const_eval_unwind_past_top);
}
// Copy return value. Must of course happen *before* we deallocate the locals.
@@ -863,7 +890,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// StorageLive expects the local to be dead, and marks it live.
let old = mem::replace(&mut self.frame_mut().locals[local].value, local_val);
if !matches!(old, LocalValue::Dead) {
- throw_ub_format!("StorageLive on a local that was already live");
+ throw_ub_custom!(fluent::const_eval_double_storage_live);
}
Ok(())
}
@@ -906,7 +933,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ErrorHandled::Reported(err) => {
if !err.is_tainted_by_errors() && let Some(span) = span {
// To make it easier to figure out where this error comes from, also add a note at the current location.
- self.tcx.sess.span_note_without_error(span, "erroneous constant used");
+ self.tcx.sess.emit_note(ErroneousConstUsed { span });
}
err_inval!(AlreadyReported(err))
}
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index c2b82ba9b..7b11ad330 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -28,6 +28,7 @@ use super::{
ValueVisitor,
};
use crate::const_eval;
+use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
pub trait CompileTimeMachine<'mir, 'tcx, T> = Machine<
'mir,
@@ -320,10 +321,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
}
}
+/// How a constant value should be interned.
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
pub enum InternKind {
/// The `mutability` of the static, ignoring the type which may have interior mutability.
Static(hir::Mutability),
+ /// A `const` item
Constant,
Promoted,
}
@@ -388,8 +391,7 @@ pub fn intern_const_alloc_recursive<
ecx.tcx.sess.delay_span_bug(
ecx.tcx.span,
format!(
- "error during interning should later cause validation failure: {}",
- error
+ "error during interning should later cause validation failure: {error:?}"
),
);
}
@@ -425,14 +427,16 @@ pub fn intern_const_alloc_recursive<
// immutability is so important.
alloc.mutability = Mutability::Not;
}
+ // If it's a constant, we should not have any "leftovers" as everything
+ // is tracked by const-checking.
+ // FIXME: downgrade this to a warning? It rejects some legitimate consts,
+ // such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
+ //
+ // NOTE: it looks likes this code path is only reachable when we try to intern
+ // something that cannot be promoted, which in constants means values that have
+ // drop glue, such as the example above.
InternKind::Constant => {
- // If it's a constant, we should not have any "leftovers" as everything
- // is tracked by const-checking.
- // FIXME: downgrade this to a warning? It rejects some legitimate consts,
- // such as `const CONST_RAW: *const Vec<i32> = &Vec::new() as *const _;`.
- ecx.tcx
- .sess
- .span_err(ecx.tcx.span, "untyped pointers are not allowed in constant");
+ ecx.tcx.sess.emit_err(UnsupportedUntypedPointer { span: ecx.tcx.span });
// For better errors later, mark the allocation as immutable.
alloc.mutability = Mutability::Not;
}
@@ -447,10 +451,7 @@ pub fn intern_const_alloc_recursive<
} else if ecx.memory.dead_alloc_map.contains_key(&alloc_id) {
// Codegen does not like dangling pointers, and generally `tcx` assumes that
// all allocations referenced anywhere actually exist. So, make sure we error here.
- let reported = ecx
- .tcx
- .sess
- .span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
+ let reported = ecx.tcx.sess.emit_err(DanglingPtrInFinal { span: ecx.tcx.span });
return Err(reported);
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
// We have hit an `AllocId` that is neither in local or global memory and isn't
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index a77c699c2..ed64a7655 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -22,6 +22,8 @@ use super::{
Pointer,
};
+use crate::fluent_generated as fluent;
+
mod caller_location;
fn numeric_intrinsic<Prov>(name: Symbol, bits: u128, kind: Primitive) -> Scalar<Prov> {
@@ -70,12 +72,12 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
}
sym::pref_align_of => {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
- let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
+ let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(*e)))?;
ConstValue::from_target_usize(layout.align.pref.bytes(), &tcx)
}
sym::type_id => {
ensure_monomorphic_enough(tcx, tp_ty)?;
- ConstValue::from_u64(tcx.type_id_hash(tp_ty).as_u64())
+ ConstValue::from_u128(tcx.type_id_hash(tp_ty).as_u128())
}
sym::variant_count => match tp_ty.kind() {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
@@ -167,8 +169,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let ty = match intrinsic_name {
sym::pref_align_of | sym::variant_count => self.tcx.types.usize,
sym::needs_drop => self.tcx.types.bool,
- sym::type_id => self.tcx.types.u64,
- sym::type_name => self.tcx.mk_static_str(),
+ sym::type_id => self.tcx.types.u128,
+ sym::type_name => Ty::new_static_str(self.tcx.tcx),
_ => bug!(),
};
let val = self.ctfe_query(None, |tcx| {
@@ -198,15 +200,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty
),
};
- let (nonzero, intrinsic_name) = match intrinsic_name {
+ let (nonzero, actual_intrinsic_name) = match intrinsic_name {
sym::cttz_nonzero => (true, sym::cttz),
sym::ctlz_nonzero => (true, sym::ctlz),
other => (false, other),
};
if nonzero && bits == 0 {
- throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name);
+ throw_ub_custom!(
+ fluent::const_eval_call_nonzero_intrinsic,
+ name = intrinsic_name,
+ );
}
- let out_val = numeric_intrinsic(intrinsic_name, bits, kind);
+ let out_val = numeric_intrinsic(actual_intrinsic_name, bits, kind);
self.write_scalar(out_val, dest)?;
}
sym::saturating_add | sym::saturating_sub => {
@@ -229,37 +234,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let r = self.read_immediate(&args[1])?;
self.exact_div(&l, &r, dest)?;
}
- sym::unchecked_shl
- | sym::unchecked_shr
- | sym::unchecked_add
- | sym::unchecked_sub
- | sym::unchecked_mul
- | sym::unchecked_div
- | sym::unchecked_rem => {
- let l = self.read_immediate(&args[0])?;
- let r = self.read_immediate(&args[1])?;
- let bin_op = match intrinsic_name {
- sym::unchecked_shl => BinOp::Shl,
- sym::unchecked_shr => BinOp::Shr,
- sym::unchecked_add => BinOp::Add,
- sym::unchecked_sub => BinOp::Sub,
- sym::unchecked_mul => BinOp::Mul,
- sym::unchecked_div => BinOp::Div,
- sym::unchecked_rem => BinOp::Rem,
- _ => bug!(),
- };
- let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, &l, &r)?;
- if overflowed {
- let layout = self.layout_of(substs.type_at(0))?;
- let r_val = r.to_scalar().to_bits(layout.size)?;
- if let sym::unchecked_shl | sym::unchecked_shr = intrinsic_name {
- throw_ub_format!("overflowing shift by {} in `{}`", r_val, intrinsic_name);
- } else {
- throw_ub_format!("overflow executing `{}`", intrinsic_name);
- }
- }
- self.write_scalar(val, dest)?;
- }
sym::rotate_left | sym::rotate_right => {
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
@@ -314,17 +288,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
(Err(_), _) | (_, Err(_)) => {
// We managed to find a valid allocation for one pointer, but not the other.
// That means they are definitely not pointing to the same allocation.
- throw_ub_format!(
- "`{}` called on pointers into different allocations",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_different_allocations,
+ name = intrinsic_name,
);
}
(Ok((a_alloc_id, a_offset, _)), Ok((b_alloc_id, b_offset, _))) => {
// Found allocation for both. They must be into the same allocation.
if a_alloc_id != b_alloc_id {
- throw_ub_format!(
- "`{}` called on pointers into different allocations",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_different_allocations,
+ name = intrinsic_name,
);
}
// Use these offsets for distance calculation.
@@ -344,11 +318,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if overflowed {
// a < b
if intrinsic_name == sym::ptr_offset_from_unsigned {
- throw_ub_format!(
- "`{}` called when first pointer has smaller offset than second: {} < {}",
- intrinsic_name,
- a_offset,
- b_offset,
+ throw_ub_custom!(
+ fluent::const_eval_unsigned_offset_from_overflow,
+ a_offset = a_offset,
+ b_offset = b_offset,
);
}
// The signed form of the intrinsic allows this. If we interpret the
@@ -356,9 +329,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// seems *positive*, they were more than isize::MAX apart.
let dist = val.to_target_isize(self)?;
if dist >= 0 {
- throw_ub_format!(
- "`{}` called when first pointer is too far before second",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_offset_from_underflow,
+ name = intrinsic_name,
);
}
dist
@@ -368,9 +341,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// If converting to isize produced a *negative* result, we had an overflow
// because they were more than isize::MAX apart.
if dist < 0 {
- throw_ub_format!(
- "`{}` called when first pointer is too far ahead of second",
- intrinsic_name
+ throw_ub_custom!(
+ fluent::const_eval_offset_from_overflow,
+ name = intrinsic_name,
);
}
dist
@@ -513,7 +486,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let op = self.eval_operand(op, None)?;
let cond = self.read_scalar(&op)?.to_bool()?;
if !cond {
- throw_ub_format!("`assume` called with `false`");
+ throw_ub_custom!(fluent::const_eval_assume_false);
}
Ok(())
}
@@ -542,7 +515,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (res, overflow, _ty) = self.overflowing_binary_op(BinOp::Rem, &a, &b)?;
assert!(!overflow); // All overflow is UB, so this should never return on overflow.
if res.assert_bits(a.layout.size) != 0 {
- throw_ub_format!("exact_div: {} cannot be divided by {} without remainder", a, b)
+ throw_ub_custom!(
+ fluent::const_eval_exact_div_has_remainder,
+ a = format!("{a}"),
+ b = format!("{b}")
+ )
}
// `Rem` says this is all right, so we can let `Div` do its job.
self.binop_ignore_overflow(BinOp::Div, &a, &b, dest)
@@ -638,9 +615,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let size = size.checked_mul(count, self).ok_or_else(|| {
- err_ub_format!(
- "overflow computing total size of `{}`",
- if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
+ err_ub_custom!(
+ fluent::const_eval_size_overflow,
+ name = if nonoverlapping { "copy_nonoverlapping" } else { "copy" }
)
})?;
@@ -664,10 +641,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// `checked_mul` enforces a too small bound (the correct one would probably be target_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
- let len = layout
- .size
- .checked_mul(count, self)
- .ok_or_else(|| err_ub_format!("overflow computing total size of `write_bytes`"))?;
+ let len = layout.size.checked_mul(count, self).ok_or_else(|| {
+ err_ub_custom!(fluent::const_eval_size_overflow, name = "write_bytes")
+ })?;
let bytes = std::iter::repeat(byte).take(len.bytes_usize());
self.write_bytes_ptr(dst, bytes)
@@ -691,7 +667,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(&[]);
};
if alloc_ref.has_provenance() {
- throw_ub_format!("`raw_eq` on bytes with provenance");
+ throw_ub_custom!(fluent::const_eval_raw_eq_with_provenance);
}
alloc_ref.get_bytes_strip_provenance()
};
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index d5b6a581a..1125d8d1f 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -19,6 +19,7 @@ use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{Align, HasDataLayout, Size};
use crate::const_eval::CheckAlignment;
+use crate::fluent_generated as fluent;
use super::{
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
@@ -200,7 +201,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
align: Align,
kind: MemoryKind<M::MemoryKind>,
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
- let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?;
+ let alloc = if M::PANIC_ON_ALLOC_FAIL {
+ Allocation::uninit(size, align)
+ } else {
+ Allocation::try_uninit(size, align)?
+ };
self.allocate_raw_ptr(alloc, kind)
}
@@ -242,9 +247,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr)?;
if offset.bytes() != 0 {
- throw_ub_format!(
- "reallocating {:?} which does not point to the beginning of an object",
- ptr
+ throw_ub_custom!(
+ fluent::const_eval_realloc_or_alloc_with_offset,
+ ptr = format!("{ptr:?}"),
+ kind = "realloc"
);
}
@@ -280,9 +286,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
trace!("deallocating: {alloc_id:?}");
if offset.bytes() != 0 {
- throw_ub_format!(
- "deallocating {:?} which does not point to the beginning of an object",
- ptr
+ throw_ub_custom!(
+ fluent::const_eval_realloc_or_alloc_with_offset,
+ ptr = format!("{ptr:?}"),
+ kind = "dealloc",
);
}
@@ -290,13 +297,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Deallocating global memory -- always an error
return Err(match self.tcx.try_get_global_alloc(alloc_id) {
Some(GlobalAlloc::Function(..)) => {
- err_ub_format!("deallocating {alloc_id:?}, which is a function")
+ err_ub_custom!(
+ fluent::const_eval_invalid_dealloc,
+ alloc_id = alloc_id,
+ kind = "fn",
+ )
}
Some(GlobalAlloc::VTable(..)) => {
- err_ub_format!("deallocating {alloc_id:?}, which is a vtable")
+ err_ub_custom!(
+ fluent::const_eval_invalid_dealloc,
+ alloc_id = alloc_id,
+ kind = "vtable",
+ )
}
Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
- err_ub_format!("deallocating {alloc_id:?}, which is static memory")
+ err_ub_custom!(
+ fluent::const_eval_invalid_dealloc,
+ alloc_id = alloc_id,
+ kind = "static_mem"
+ )
}
None => err_ub!(PointerUseAfterFree(alloc_id)),
}
@@ -304,21 +323,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
if alloc.mutability.is_not() {
- throw_ub_format!("deallocating immutable allocation {alloc_id:?}");
+ throw_ub_custom!(fluent::const_eval_dealloc_immutable, alloc = alloc_id,);
}
if alloc_kind != kind {
- throw_ub_format!(
- "deallocating {alloc_id:?}, which is {alloc_kind} memory, using {kind} deallocation operation"
+ throw_ub_custom!(
+ fluent::const_eval_dealloc_kind_mismatch,
+ alloc = alloc_id,
+ alloc_kind = format!("{alloc_kind}"),
+ kind = format!("{kind}"),
);
}
if let Some((size, align)) = old_size_and_align {
if size != alloc.size() || align != alloc.align {
- throw_ub_format!(
- "incorrect layout on deallocation: {alloc_id:?} has size {} and alignment {}, but gave size {} and alignment {}",
- alloc.size().bytes(),
- alloc.align.bytes(),
- size.bytes(),
- align.bytes(),
+ throw_ub_custom!(
+ fluent::const_eval_dealloc_incorrect_layout,
+ alloc = alloc_id,
+ size = alloc.size().bytes(),
+ align = alloc.align.bytes(),
+ size_found = size.bytes(),
+ align_found = align.bytes(),
)
}
}
@@ -1166,7 +1189,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if (src_offset <= dest_offset && src_offset + size > dest_offset)
|| (dest_offset <= src_offset && dest_offset + size > src_offset)
{
- throw_ub_format!("copy_nonoverlapping called on overlapping ranges")
+ throw_ub_custom!(fluent::const_eval_copy_nonoverlapping_overlapping);
}
}
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index e30af1655..5f89d652f 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -106,7 +106,7 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
// Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to
// print what is points to, which would fail since it has no access to the local
// memory.
- cx.pretty_print_const_pointer(ptr, ty, true)
+ cx.pretty_print_const_pointer(ptr, ty)
}
}
}
@@ -633,7 +633,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}
- pub(super) fn const_val_to_op(
+ pub(crate) fn const_val_to_op(
&self,
val_val: ConstValue<'tcx>,
ty: Ty<'tcx>,
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index 7186148da..e04764636 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -3,10 +3,13 @@ use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::symbol::sym;
use rustc_target::abi::Abi;
use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy};
+use crate::fluent_generated as fluent;
+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
/// and a boolean signifying the potential overflow to the destination.
@@ -19,7 +22,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx> {
let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?;
debug_assert_eq!(
- self.tcx.mk_tup(&[ty, self.tcx.types.bool]),
+ Ty::new_tup(self.tcx.tcx, &[ty, self.tcx.types.bool]),
dest.layout.ty,
"type mismatch for result of {:?}",
op,
@@ -139,8 +142,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
) -> InterpResult<'tcx, (Scalar<M::Provenance>, bool, Ty<'tcx>)> {
use rustc_middle::mir::BinOp::*;
+ let throw_ub_on_overflow = match bin_op {
+ AddUnchecked => Some(sym::unchecked_add),
+ SubUnchecked => Some(sym::unchecked_sub),
+ MulUnchecked => Some(sym::unchecked_mul),
+ ShlUnchecked => Some(sym::unchecked_shl),
+ ShrUnchecked => Some(sym::unchecked_shr),
+ _ => None,
+ };
+
// Shift ops can have an RHS with a different numeric type.
- if bin_op == Shl || bin_op == Shr {
+ if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
let size = u128::from(left_layout.size.bits());
// Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
// zero-extended form). This matches the codegen backend:
@@ -155,6 +167,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// integers are maximally 128bits wide, so negative shifts *always* overflow and we have
// consistent results for the same value represented at different bit widths.
assert!(size <= 128);
+ let original_r = r;
let overflow = r >= size;
// The shift offset is implicitly masked to the type size, to make sure this operation
// is always defined. This is the one MIR operator that does *not* directly map to a
@@ -166,19 +179,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let result = if left_layout.abi.is_signed() {
let l = self.sign_extend(l, left_layout) as i128;
let result = match bin_op {
- Shl => l.checked_shl(r).unwrap(),
- Shr => l.checked_shr(r).unwrap(),
+ Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+ Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
_ => bug!(),
};
result as u128
} else {
match bin_op {
- Shl => l.checked_shl(r).unwrap(),
- Shr => l.checked_shr(r).unwrap(),
+ Shl | ShlUnchecked => l.checked_shl(r).unwrap(),
+ Shr | ShrUnchecked => l.checked_shr(r).unwrap(),
_ => bug!(),
}
};
let truncated = self.truncate(result, left_layout);
+
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(
+ fluent::const_eval_overflow_shift,
+ val = original_r,
+ name = intrinsic_name
+ );
+ }
+
return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty));
}
@@ -216,9 +238,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Rem if r == 0 => throw_ub!(RemainderByZero),
Div => Some(i128::overflowing_div),
Rem => Some(i128::overflowing_rem),
- Add => Some(i128::overflowing_add),
- Sub => Some(i128::overflowing_sub),
- Mul => Some(i128::overflowing_mul),
+ Add | AddUnchecked => Some(i128::overflowing_add),
+ Sub | SubUnchecked => Some(i128::overflowing_sub),
+ Mul | MulUnchecked => Some(i128::overflowing_mul),
_ => None,
};
if let Some(op) = op {
@@ -242,11 +264,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// If that truncation loses any information, we have an overflow.
let result = result as u128;
let truncated = self.truncate(result, left_layout);
- return Ok((
- Scalar::from_uint(truncated, size),
- oflo || self.sign_extend(truncated, left_layout) != result,
- left_layout.ty,
- ));
+ let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+ }
+ return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
}
}
@@ -263,12 +285,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty),
BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty),
- Add | Sub | Mul | Rem | Div => {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
assert!(!left_layout.abi.is_signed());
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
- Add => u128::overflowing_add,
- Sub => u128::overflowing_sub,
- Mul => u128::overflowing_mul,
+ Add | AddUnchecked => u128::overflowing_add,
+ Sub | SubUnchecked => u128::overflowing_sub,
+ Mul | MulUnchecked => u128::overflowing_mul,
Div if r == 0 => throw_ub!(DivisionByZero),
Rem if r == 0 => throw_ub!(RemainderByZero),
Div => u128::overflowing_div,
@@ -279,11 +301,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Truncate to target type.
// If that truncation loses any information, we have an overflow.
let truncated = self.truncate(result, left_layout);
- return Ok((
- Scalar::from_uint(truncated, size),
- oflo || truncated != result,
- left_layout.ty,
- ));
+ let overflow = oflo || truncated != result;
+ if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
+ throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
+ }
+ return Ok((Scalar::from_uint(truncated, size), overflow, left_layout.ty));
}
_ => span_bug!(
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 2a31a59ad..ca1106384 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -9,6 +9,7 @@ use rustc_index::IndexSlice;
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT};
use super::{
@@ -395,7 +396,7 @@ where
// (Transmuting is okay since this is an in-memory place. We also double-check the size
// stays the same.)
let (len, e_ty) = mplace.layout.ty.simd_size_and_type(*self.tcx);
- let array = self.tcx.mk_array(e_ty, len);
+ let array = Ty::new_array(self.tcx.tcx, e_ty, len);
let layout = self.layout_of(array)?;
assert_eq!(layout.size, mplace.layout.size);
Ok((MPlaceTy { layout, ..*mplace }, len))
@@ -699,8 +700,13 @@ where
assert_eq!(src.layout.size, dest.layout.size);
}
+ // Setting `nonoverlapping` here only has an effect when we don't hit the fast-path above,
+ // but that should at least match what LLVM does where `memcpy` is also only used when the
+ // type does not have Scalar/ScalarPair layout.
+ // (Or as the `Assign` docs put it, assignments "not producing primitives" must be
+ // non-overlapping.)
self.mem_copy(
- src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ false,
+ src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ true,
)
}
@@ -775,7 +781,8 @@ where
let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
let mplace = MemPlace { ptr: ptr.into(), meta: MemPlaceMeta::Meta(meta) };
- let ty = self.tcx.mk_ref(
+ let ty = Ty::new_ref(
+ self.tcx.tcx,
self.tcx.lifetimes.re_static,
ty::TypeAndMut { ty: self.tcx.types.str_, mutbl },
);
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 91da930db..d7d31fe18 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -12,6 +12,7 @@ use either::{Left, Right};
use rustc_middle::mir;
use rustc_middle::ty;
use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::Ty;
use rustc_target::abi::{self, Abi, VariantIdx};
use super::{
@@ -317,7 +318,9 @@ where
let (meta, ty) = match base.layout.ty.kind() {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
- ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
+ ty::Array(inner, _) => {
+ (MemPlaceMeta::None, Ty::new_array(self.tcx.tcx, *inner, inner_len))
+ }
ty::Slice(..) => {
let len = Scalar::from_target_usize(inner_len, self);
(MemPlaceMeta::Meta(len), base.layout.ty)
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1e60a1e72..619da8abb 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -9,27 +9,7 @@ use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::LayoutOf;
use super::{ImmTy, InterpCx, Machine};
-
-/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
-/// same type as the result.
-#[inline]
-fn binop_left_homogeneous(op: mir::BinOp) -> bool {
- use rustc_middle::mir::BinOp::*;
- match op {
- Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Offset | Shl | Shr => true,
- Eq | Ne | Lt | Le | Gt | Ge => false,
- }
-}
-/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
-/// same type as the LHS.
-#[inline]
-fn binop_right_homogeneous(op: mir::BinOp) -> bool {
- use rustc_middle::mir::BinOp::*;
- match op {
- Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
- Offset | Shl | Shr => false,
- }
-}
+use crate::util;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Returns `true` as long as there are more things to do.
@@ -179,9 +159,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
BinaryOp(bin_op, box (ref left, ref right)) => {
- let layout = binop_left_homogeneous(bin_op).then_some(dest.layout);
+ let layout = util::binop_left_homogeneous(bin_op).then_some(dest.layout);
let left = self.read_immediate(&self.eval_operand(left, layout)?)?;
- let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+ let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
self.binop_ignore_overflow(bin_op, &left, &right, &dest)?;
}
@@ -189,7 +169,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
CheckedBinaryOp(bin_op, box (ref left, ref right)) => {
// Due to the extra boolean in the result, we can never reuse the `dest.layout`.
let left = self.read_immediate(&self.eval_operand(left, None)?)?;
- let layout = binop_right_homogeneous(bin_op).then_some(left.layout);
+ let layout = util::binop_right_homogeneous(bin_op).then_some(left.layout);
let right = self.read_immediate(&self.eval_operand(right, layout)?)?;
self.binop_with_overflow(bin_op, &left, &right, &dest)?;
}
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 586e8f063..15823a597 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -15,6 +15,7 @@ use super::{
FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand,
PlaceTy, Scalar, StackPopCleanup,
};
+use crate::fluent_generated as fluent;
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn eval_terminator(
@@ -61,7 +62,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
destination,
target,
unwind,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
let old_stack = self.frame_idx();
@@ -172,7 +173,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
InlineAsm { template, ref operands, options, destination, .. } => {
M::eval_inline_asm(self, template, operands, options)?;
if options.contains(InlineAsmOptions::NORETURN) {
- throw_ub_format!("returned from noreturn inline assembly");
+ throw_ub_custom!(fluent::const_eval_noreturn_asm_returned);
}
self.go_to_block(
destination
@@ -288,15 +289,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(());
}
// Find next caller arg.
- let (caller_arg, caller_abi) = caller_args.next().ok_or_else(|| {
- err_ub_format!("calling a function with fewer arguments than it requires")
- })?;
+ let Some((caller_arg, caller_abi)) = caller_args.next() else {
+ throw_ub_custom!(fluent::const_eval_not_enough_caller_args);
+ };
// Now, check
if !Self::check_argument_compat(caller_abi, callee_abi) {
- throw_ub_format!(
- "calling a function with argument of type {:?} passing data of type {:?}",
- callee_arg.layout.ty,
- caller_arg.layout.ty
+ let callee_ty = format!("{}", callee_arg.layout.ty);
+ let caller_ty = format!("{}", caller_arg.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_types,
+ callee_ty = callee_ty,
+ caller_ty = caller_ty,
)
}
// Special handling for unsized parameters.
@@ -398,10 +401,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
if M::enforce_abi(self) {
if caller_fn_abi.conv != callee_fn_abi.conv {
- throw_ub_format!(
- "calling a function with calling convention {:?} using calling convention {:?}",
- callee_fn_abi.conv,
- caller_fn_abi.conv
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_calling_conventions,
+ callee_conv = format!("{:?}", callee_fn_abi.conv),
+ caller_conv = format!("{:?}", caller_fn_abi.conv),
)
}
}
@@ -508,15 +511,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
"mismatch between callee ABI and callee body arguments"
);
if caller_args.next().is_some() {
- throw_ub_format!("calling a function with more arguments than it expected")
+ throw_ub_custom!(fluent::const_eval_too_many_caller_args);
}
// Don't forget to check the return type!
if !Self::check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret) {
- throw_ub_format!(
- "calling a function with return type {:?} passing \
- return place of type {:?}",
- callee_fn_abi.ret.layout.ty,
- caller_fn_abi.ret.layout.ty,
+ let callee_ty = format!("{}", callee_fn_abi.ret.layout.ty);
+ let caller_ty = format!("{}", caller_fn_abi.ret.layout.ty);
+ throw_ub_custom!(
+ fluent::const_eval_incompatible_return_types,
+ callee_ty = callee_ty,
+ caller_ty = caller_ty,
)
}
};
@@ -587,9 +591,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (recv, vptr) = self.unpack_dyn_star(&receiver_place.into())?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
- throw_ub_format!(
- "`dyn*` call on a pointer whose vtable does not match its type"
- );
+ throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
}
let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
@@ -609,9 +611,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
if dyn_trait != data.principal() {
- throw_ub_format!(
- "`dyn` call on a pointer whose vtable does not match its type"
- );
+ throw_ub_custom!(fluent::const_eval_dyn_call_vtable_mismatch);
}
// It might be surprising that we use a pointer as the receiver even if this
@@ -623,7 +623,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Now determine the actual method to call. We can do that in two different ways and
// compare them to ensure everything fits.
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
- throw_ub_format!("`dyn` call trying to call something that is not a method")
+ // FIXME(fee1-dead) these could be variants of the UB info enum instead of this
+ throw_ub_custom!(fluent::const_eval_dyn_call_not_a_method);
};
trace!("Virtual call dispatches to {fn_inst:#?}");
if cfg!(debug_assertions) {
@@ -649,7 +650,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Adjust receiver argument. Layout can be any (thin) ptr.
args[0] = ImmTy::from_immediate(
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
- self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
+ self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, dyn_ty))?,
)
.into();
trace!("Patched receiver operand to {:#?}", args[0]);
@@ -702,7 +703,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let arg = ImmTy::from_immediate(
place.to_ref(self),
- self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
+ self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?,
);
let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?);
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 01b772899..21c655988 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -4,7 +4,7 @@
//! That's useful because it means other passes (e.g. promotion) can rely on `const`s
//! to be const-safe.
-use std::fmt::{Display, Write};
+use std::fmt::Write;
use std::num::NonZeroUsize;
use either::{Left, Right};
@@ -12,7 +12,10 @@ use either::{Left, Right};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
-use rustc_middle::mir::interpret::InterpError;
+use rustc_middle::mir::interpret::{
+ ExpectedKind, InterpError, InvalidMetaKind, PointerKind, ValidationErrorInfo,
+ ValidationErrorKind, ValidationErrorKind::*,
+};
use rustc_middle::ty;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_span::symbol::{sym, Symbol};
@@ -30,14 +33,7 @@ use super::{
};
macro_rules! throw_validation_failure {
- ($where:expr, { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )?) => {{
- let mut msg = String::new();
- msg.push_str("encountered ");
- write!(&mut msg, $($what_fmt)*).unwrap();
- $(
- msg.push_str(", but expected ");
- write!(&mut msg, $($expected_fmt)*).unwrap();
- )?
+ ($where:expr, $kind: expr) => {{
let where_ = &$where;
let path = if !where_.is_empty() {
let mut path = String::new();
@@ -46,7 +42,8 @@ macro_rules! throw_validation_failure {
} else {
None
};
- throw_ub!(ValidationFailure { path, msg })
+
+ throw_ub!(Validation(ValidationErrorInfo { path, kind: $kind }))
}};
}
@@ -82,22 +79,22 @@ macro_rules! throw_validation_failure {
///
macro_rules! try_validation {
($e:expr, $where:expr,
- $( $( $p:pat_param )|+ => { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )? ),+ $(,)?
+ $( $( $p:pat_param )|+ => $kind: expr ),+ $(,)?
) => {{
match $e {
Ok(x) => x,
// We catch the error and turn it into a validation failure. We are okay with
// allocation here as this can only slow down builds that fail anyway.
- Err(e) => match e.kind() {
+ Err(e) => match e.into_parts() {
$(
- InterpError::UndefinedBehavior($($p)|+) =>
+ (InterpError::UndefinedBehavior($($p)|+), _) =>
throw_validation_failure!(
$where,
- { $( $what_fmt )* } $( expected { $( $expected_fmt )* } )?
+ $kind
)
),+,
#[allow(unreachable_patterns)]
- _ => Err::<!, _>(e)?,
+ (e, rest) => Err::<!, _>($crate::interpret::InterpErrorInfo::from_parts(e, rest))?,
}
}
}};
@@ -160,6 +157,7 @@ impl<T: Copy + Eq + Hash + std::fmt::Debug, PATH: Default> RefTracking<T, PATH>
}
}
+// FIXME make this translatable as well?
/// Format a path
fn write_path(out: &mut String, path: &[PathElem]) {
use self::PathElem::*;
@@ -185,26 +183,6 @@ fn write_path(out: &mut String, path: &[PathElem]) {
}
}
-// Formats such that a sentence like "expected something {}" to mean
-// "expected something <in the given range>" makes sense.
-fn wrapping_range_format(r: WrappingRange, max_hi: u128) -> String {
- let WrappingRange { start: lo, end: hi } = r;
- assert!(hi <= max_hi);
- if lo > hi {
- format!("less or equal to {}, or greater or equal to {}", hi, lo)
- } else if lo == hi {
- format!("equal to {}", lo)
- } else if lo == 0 {
- assert!(hi < max_hi, "should not be printing if the range covers everything");
- format!("less or equal to {}", hi)
- } else if hi == max_hi {
- assert!(lo > 0, "should not be printing if the range covers everything");
- format!("greater or equal to {}", lo)
- } else {
- format!("in the range {:?}", r)
- }
-}
-
struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
/// The `path` may be pushed to, but the part that is present when a function
/// starts must not be changed! `visit_fields` and `visit_array` rely on
@@ -311,19 +289,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
fn read_immediate(
&self,
op: &OpTy<'tcx, M::Provenance>,
- expected: impl Display,
+ expected: ExpectedKind,
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
Ok(try_validation!(
self.ecx.read_immediate(op),
self.path,
- InvalidUninitBytes(None) => { "uninitialized memory" } expected { "{expected}" }
+ InvalidUninitBytes(None) => Uninit { expected }
))
}
fn read_scalar(
&self,
op: &OpTy<'tcx, M::Provenance>,
- expected: impl Display,
+ expected: ExpectedKind,
) -> InterpResult<'tcx, Scalar<M::Provenance>> {
Ok(self.read_immediate(op, expected)?.to_scalar())
}
@@ -342,8 +320,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
self.ecx.get_ptr_vtable(vtable),
self.path,
DanglingIntPointer(..) |
- InvalidVTablePointer(..) =>
- { "{vtable}" } expected { "a vtable pointer" },
+ InvalidVTablePointer(..) => InvalidVTablePtr { value: format!("{vtable}") }
);
// FIXME: check if the type/trait match what ty::Dynamic says?
}
@@ -366,10 +343,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
fn check_safe_pointer(
&mut self,
value: &OpTy<'tcx, M::Provenance>,
- kind: &str,
+ ptr_kind: PointerKind,
) -> InterpResult<'tcx> {
- let place =
- self.ecx.ref_to_mplace(&self.read_immediate(value, format_args!("a {kind}"))?)?;
+ let place = self.ecx.ref_to_mplace(&self.read_immediate(value, ptr_kind.into())?)?;
// Handle wide pointers.
// Check metadata early, for better diagnostics
if place.layout.is_unsized() {
@@ -379,7 +355,10 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let size_and_align = try_validation!(
self.ecx.size_and_align_of_mplace(&place),
self.path,
- InvalidMeta(msg) => { "invalid {} metadata: {}", kind, msg },
+ InvalidMeta(msg) => match msg {
+ InvalidMetaKind::SliceTooBig => InvalidMetaSliceTooLarge { ptr_kind },
+ InvalidMetaKind::TooBig => InvalidMetaTooLarge { ptr_kind },
+ }
);
let (size, align) = size_and_align
// for the purpose of validity, consider foreign types to have
@@ -395,31 +374,30 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
),
self.path,
- AlignmentCheckFailed { required, has } =>
- {
- "an unaligned {kind} (required {} byte alignment but found {})",
- required.bytes(),
- has.bytes(),
- },
- DanglingIntPointer(0, _) =>
- { "a null {kind}" },
- DanglingIntPointer(i, _) =>
- {
- "a dangling {kind} ({pointer} has no provenance)",
- pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
- },
- PointerOutOfBounds { .. } =>
- { "a dangling {kind} (going beyond the bounds of its allocation)" },
+ AlignmentCheckFailed { required, has } => UnalignedPtr {
+ ptr_kind,
+ required_bytes: required.bytes(),
+ found_bytes: has.bytes()
+ },
+ DanglingIntPointer(0, _) => NullPtr { ptr_kind },
+ DanglingIntPointer(i, _) => DanglingPtrNoProvenance {
+ ptr_kind,
+ // FIXME this says "null pointer" when null but we need translate
+ pointer: format!("{}", Pointer::<Option<AllocId>>::from_addr_invalid(i))
+ },
+ PointerOutOfBounds { .. } => DanglingPtrOutOfBounds {
+ ptr_kind
+ },
// This cannot happen during const-eval (because interning already detects
// dangling pointers), but it can happen in Miri.
- PointerUseAfterFree(..) =>
- { "a dangling {kind} (use-after-free)" },
+ PointerUseAfterFree(..) => DanglingPtrUseAfterFree {
+ ptr_kind,
+ },
);
// Do not allow pointers to uninhabited types.
if place.layout.abi.is_uninhabited() {
- throw_validation_failure!(self.path,
- { "a {kind} pointing to uninhabited type {}", place.layout.ty }
- )
+ let ty = place.layout.ty;
+ throw_validation_failure!(self.path, PtrToUninhabited { ptr_kind, ty })
}
// Recursive checking
if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
@@ -441,9 +419,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// this check is so important.
// This check is reachable when the const just referenced the static,
// but never read it (so we never entered `before_access_global`).
- throw_validation_failure!(self.path,
- { "a {} pointing to a static variable in a constant", kind }
- );
+ throw_validation_failure!(self.path, PtrToStatic { ptr_kind });
}
// We skip recursively checking other statics. These statics must be sound by
// themselves, and the only way to get broken statics here is by using
@@ -464,9 +440,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// This should be unreachable, but if someone manages to copy a pointer
// out of a `static`, then that pointer might point to mutable memory,
// and we would catch that here.
- throw_validation_failure!(self.path,
- { "a {} pointing to mutable memory in a constant", kind }
- );
+ throw_validation_failure!(self.path, PtrToMut { ptr_kind });
}
}
// Nothing to check for these.
@@ -496,22 +470,24 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let ty = value.layout.ty;
match ty.kind() {
ty::Bool => {
- let value = self.read_scalar(value, "a boolean")?;
+ let value = self.read_scalar(value, ExpectedKind::Bool)?;
try_validation!(
value.to_bool(),
self.path,
- InvalidBool(..) =>
- { "{:x}", value } expected { "a boolean" },
+ InvalidBool(..) => ValidationErrorKind::InvalidBool {
+ value: format!("{value:x}"),
+ }
);
Ok(true)
}
ty::Char => {
- let value = self.read_scalar(value, "a unicode scalar value")?;
+ let value = self.read_scalar(value, ExpectedKind::Char)?;
try_validation!(
value.to_char(),
self.path,
- InvalidChar(..) =>
- { "{:x}", value } expected { "a valid unicode scalar value (in `0..=0x10FFFF` but not in `0xD800..=0xDFFF`)" },
+ InvalidChar(..) => ValidationErrorKind::InvalidChar {
+ value: format!("{value:x}"),
+ }
);
Ok(true)
}
@@ -521,16 +497,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
let value = self.read_scalar(
value,
if matches!(ty.kind(), ty::Float(..)) {
- "a floating point number"
+ ExpectedKind::Float
} else {
- "an integer"
+ ExpectedKind::Int
},
)?;
// As a special exception we *do* match on a `Scalar` here, since we truly want
// to know its underlying representation (and *not* cast it to an integer).
if matches!(value, Scalar::Ptr(..)) {
- throw_validation_failure!(self.path,
- { "{:x}", value } expected { "plain (non-pointer) bytes" }
+ throw_validation_failure!(
+ self.path,
+ ExpectedNonPtr { value: format!("{value:x}") }
)
}
Ok(true)
@@ -540,7 +517,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// actually enforce the strict rules for raw pointers (mostly because
// that lets us re-use `ref_to_mplace`).
let place =
- self.ecx.ref_to_mplace(&self.read_immediate(value, "a raw pointer")?)?;
+ self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?;
if place.layout.is_unsized() {
self.check_wide_ptr_meta(place.meta, place.layout)?;
}
@@ -554,14 +531,14 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
// a ZST).
let layout = self.ecx.layout_of(*ty)?;
if !layout.is_zst() {
- throw_validation_failure!(self.path, { "mutable reference in a `const`" });
+ throw_validation_failure!(self.path, MutableRefInConst);
}
}
- self.check_safe_pointer(value, "reference")?;
+ self.check_safe_pointer(value, PointerKind::Ref)?;
Ok(true)
}
ty::FnPtr(_sig) => {
- let value = self.read_scalar(value, "a function pointer")?;
+ let value = self.read_scalar(value, ExpectedKind::FnPtr)?;
// If we check references recursively, also check that this points to a function.
if let Some(_) = self.ref_tracking {
@@ -570,19 +547,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
self.ecx.get_ptr_fn(ptr),
self.path,
DanglingIntPointer(..) |
- InvalidFunctionPointer(..) =>
- { "{ptr}" } expected { "a function pointer" },
+ InvalidFunctionPointer(..) => InvalidFnPtr {
+ value: format!("{ptr}"),
+ },
);
// FIXME: Check if the signature matches
} else {
// Otherwise (for standalone Miri), we have to still check it to be non-null.
if self.ecx.scalar_may_be_null(value)? {
- throw_validation_failure!(self.path, { "a null function pointer" });
+ throw_validation_failure!(self.path, NullFnPtr);
}
}
Ok(true)
}
- ty::Never => throw_validation_failure!(self.path, { "a value of the never type `!`" }),
+ ty::Never => throw_validation_failure!(self.path, NeverVal),
ty::Foreign(..) | ty::FnDef(..) => {
// Nothing to check.
Ok(true)
@@ -629,12 +607,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if start == 1 && end == max_value {
// Only null is the niche. So make sure the ptr is NOT null.
if self.ecx.scalar_may_be_null(scalar)? {
- throw_validation_failure!(self.path,
- { "a potentially null pointer" }
- expected {
- "something that cannot possibly fail to be {}",
- wrapping_range_format(valid_range, max_value)
- }
+ throw_validation_failure!(
+ self.path,
+ NullablePtrOutOfRange { range: valid_range, max_value }
)
} else {
return Ok(());
@@ -645,12 +620,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
} else {
// Conservatively, we reject, because the pointer *could* have a bad
// value.
- throw_validation_failure!(self.path,
- { "a pointer" }
- expected {
- "something that cannot possibly fail to be {}",
- wrapping_range_format(valid_range, max_value)
- }
+ throw_validation_failure!(
+ self.path,
+ PtrOutOfRange { range: valid_range, max_value }
)
}
}
@@ -659,9 +631,9 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
if valid_range.contains(bits) {
Ok(())
} else {
- throw_validation_failure!(self.path,
- { "{}", bits }
- expected { "something {}", wrapping_range_format(valid_range, max_value) }
+ throw_validation_failure!(
+ self.path,
+ OutOfRange { value: format!("{bits}"), range: valid_range, max_value }
)
}
}
@@ -685,10 +657,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Ok(try_validation!(
this.ecx.read_discriminant(op),
this.path,
- InvalidTag(val) =>
- { "{:x}", val } expected { "a valid enum tag" },
- InvalidUninitBytes(None) =>
- { "uninitialized bytes" } expected { "a valid enum tag" },
+ InvalidTag(val) => InvalidEnumTag {
+ value: format!("{val:x}"),
+ },
+
+ InvalidUninitBytes(None) => UninitEnumTag,
)
.1)
})
@@ -730,7 +703,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// Special check preventing `UnsafeCell` inside unions in the inner part of constants.
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. })) {
if !op.layout.ty.is_freeze(*self.ecx.tcx, self.ecx.param_env) {
- throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" });
+ throw_validation_failure!(self.path, UnsafeCell);
}
}
Ok(())
@@ -738,7 +711,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
#[inline]
fn visit_box(&mut self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx> {
- self.check_safe_pointer(op, "box")?;
+ self.check_safe_pointer(op, PointerKind::Box)?;
Ok(())
}
@@ -756,7 +729,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true, .. }))
&& def.is_unsafe_cell()
{
- throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" });
+ throw_validation_failure!(self.path, UnsafeCell);
}
}
@@ -775,14 +748,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// MyNewtype and then the scalar in there).
match op.layout.abi {
Abi::Uninhabited => {
- throw_validation_failure!(self.path,
- { "a value of uninhabited type {:?}", op.layout.ty }
- );
+ let ty = op.layout.ty;
+ throw_validation_failure!(self.path, UninhabitedVal { ty });
}
Abi::Scalar(scalar_layout) => {
if !scalar_layout.is_uninit_valid() {
// There is something to check here.
- let scalar = self.read_scalar(op, "initialized scalar value")?;
+ let scalar = self.read_scalar(op, ExpectedKind::InitScalar)?;
self.visit_scalar(scalar, scalar_layout)?;
}
}
@@ -792,7 +764,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
// the other must be init.
if !a_layout.is_uninit_valid() && !b_layout.is_uninit_valid() {
let (a, b) =
- self.read_immediate(op, "initialized scalar value")?.to_scalar_pair();
+ self.read_immediate(op, ExpectedKind::InitScalar)?.to_scalar_pair();
self.visit_scalar(a, a_layout)?;
self.visit_scalar(b, b_layout)?;
}
@@ -822,7 +794,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
try_validation!(
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)),
self.path,
- InvalidUninitBytes(..) => { "uninitialized data in `str`" },
+ InvalidUninitBytes(..) => { UninitStr },
);
}
ty::Array(tys, ..) | ty::Slice(tys)
@@ -852,7 +824,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
Left(mplace) => mplace,
Right(imm) => match *imm {
Immediate::Uninit =>
- throw_validation_failure!(self.path, { "uninitialized bytes" }),
+ throw_validation_failure!(self.path, UninitVal),
Immediate::Scalar(..) | Immediate::ScalarPair(..) =>
bug!("arrays/slices can never have Scalar/ScalarPair layout"),
}
@@ -888,7 +860,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
.unwrap();
self.path.push(PathElem::ArrayElem(i));
- throw_validation_failure!(self.path, { "uninitialized bytes" })
+ throw_validation_failure!(self.path, UninitVal)
}
// Propagate upwards (that will also check for unexpected errors).
@@ -929,12 +901,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
match visitor.visit_value(&op) {
Ok(()) => Ok(()),
// Pass through validation failures.
- Err(err) if matches!(err.kind(), err_ub!(ValidationFailure { .. })) => Err(err),
+ Err(err) if matches!(err.kind(), err_ub!(Validation { .. })) => Err(err),
// Complain about any other kind of UB error -- those are bad because we'd like to
// report them in a way that shows *where* in the value the issue lies.
Err(err) if matches!(err.kind(), InterpError::UndefinedBehavior(_)) => {
- err.print_backtrace();
- bug!("Unexpected Undefined Behavior error during validation: {}", err);
+ let (err, backtrace) = err.into_parts();
+ backtrace.print_backtrace();
+ bug!("Unexpected Undefined Behavior error during validation: {err:?}");
}
// Pass through everything else.
Err(err) => Err(err),
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index c36282d5e..c126f749b 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -4,6 +4,7 @@ Rust MIR: a lowered representation of Rust.
*/
+#![deny(rustc::untranslatable_diagnostic)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(decl_macro)]
@@ -33,6 +34,8 @@ pub mod interpret;
pub mod transform;
pub mod util;
+pub use errors::ReportErrorExt;
+
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_middle::query::Providers;
@@ -49,17 +52,11 @@ pub fn provide(providers: &mut Providers) {
let (param_env, raw) = param_env_and_value.into_parts();
const_eval::eval_to_valtree(tcx, param_env, raw)
};
- providers.try_destructure_mir_constant = |tcx, param_env_and_value| {
- let (param_env, value) = param_env_and_value.into_parts();
- const_eval::try_destructure_mir_constant(tcx, param_env, value).ok()
- };
+ providers.try_destructure_mir_constant_for_diagnostics =
+ |tcx, (cv, ty)| const_eval::try_destructure_mir_constant_for_diagnostics(tcx, cv, ty);
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
const_eval::valtree_to_const_value(tcx, ty::ParamEnv::empty().and(ty), valtree)
};
- providers.deref_mir_constant = |tcx, param_env_and_value| {
- let (param_env, value) = param_env_and_value.into_parts();
- const_eval::deref_mir_constant(tcx, param_env, value)
- };
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)
};
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 57d939747..14540e8df 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -9,8 +9,8 @@ use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
-use rustc_middle::ty::{self, adjustment::PointerCast, Instance, InstanceDef, Ty, TyCtxt};
-use rustc_middle::ty::{Binder, TraitRef, TypeVisitableExt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Instance, InstanceDef, Ty, TyCtxt};
+use rustc_middle::ty::{TraitRef, TypeVisitableExt};
use rustc_mir_dataflow::{self, Analysis};
use rustc_span::{sym, Span, Symbol};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@@ -412,7 +412,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
BorrowKind::Shallow => {
PlaceContext::NonMutatingUse(NonMutatingUseContext::ShallowBorrow)
}
- BorrowKind::Unique => PlaceContext::MutatingUse(MutatingUseContext::Borrow),
BorrowKind::Mut { .. } => {
PlaceContext::MutatingUse(MutatingUseContext::Borrow)
}
@@ -457,7 +456,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
}
- Rvalue::Ref(_, kind @ (BorrowKind::Mut { .. } | BorrowKind::Unique), place) => {
+ Rvalue::Ref(_, BorrowKind::Mut { .. }, place) => {
let ty = place.ty(self.body, self.tcx).ty;
let is_allowed = match ty.kind() {
// Inside a `static mut`, `&mut [...]` is allowed.
@@ -478,11 +477,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
};
if !is_allowed {
- if let BorrowKind::Mut { .. } = kind {
- self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
- } else {
- self.check_op(ops::CellBorrow);
- }
+ self.check_mut_borrow(place.local, hir::BorrowKind::Ref)
}
}
@@ -526,12 +521,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
Rvalue::Cast(
- CastKind::Pointer(
- PointerCast::MutToConstPointer
- | PointerCast::ArrayToPointer
- | PointerCast::UnsafeFnPointer
- | PointerCast::ClosureFnPointer(_)
- | PointerCast::ReifyFnPointer,
+ CastKind::PointerCoercion(
+ PointerCoercion::MutToConstPointer
+ | PointerCoercion::ArrayToPointer
+ | PointerCoercion::UnsafeFnPointer
+ | PointerCoercion::ClosureFnPointer(_)
+ | PointerCoercion::ReifyFnPointer,
),
_,
_,
@@ -539,7 +534,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// These are all okay; they only change the type, not the data.
}
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
+ Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), _, _) => {
// Unsizing is implemented for CTFE.
}
@@ -617,30 +612,28 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
}
fn visit_projection_elem(
&mut self,
- place_local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
trace!(
- "visit_projection_elem: place_local={:?} proj_base={:?} elem={:?} \
+ "visit_projection_elem: place_ref={:?} elem={:?} \
context={:?} location={:?}",
- place_local,
- proj_base,
+ place_ref,
elem,
context,
location,
);
- self.super_projection_elem(place_local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
match elem {
ProjectionElem::Deref => {
- let base_ty = Place::ty_from(place_local, proj_base, self.body, self.tcx).ty;
+ let base_ty = place_ref.ty(self.body, self.tcx).ty;
if base_ty.is_unsafe_ptr() {
- if proj_base.is_empty() {
- let decl = &self.body.local_decls[place_local];
+ if place_ref.projection.is_empty() {
+ let decl = &self.body.local_decls[place_ref.local];
if let LocalInfo::StaticRef { def_id, .. } = *decl.local_info() {
let span = decl.source_info.span;
self.check_static(def_id, span);
@@ -702,7 +695,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
self.super_terminator(terminator, location);
match &terminator.kind {
- TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => {
+ TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
let caller = self.def_id();
@@ -755,17 +748,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: Some(sym::const_trait_impl),
});
return;
}
let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
- let poly_trait_pred =
- Binder::dummy(trait_ref).with_constness(ty::BoundConstness::ConstIfConst);
+ let trait_ref = trait_ref.with_constness(ty::BoundConstness::ConstIfConst);
let obligation =
- Obligation::new(tcx, ObligationCause::dummy(), param_env, poly_trait_pred);
+ Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
let implsrc = {
let infcx = tcx.infer_ctxt().build();
@@ -781,14 +773,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
);
return;
}
- Ok(Some(ImplSource::Closure(data))) => {
- if !tcx.is_const_fn_raw(data.closure_def_id) {
+ // Closure: Fn{Once|Mut}
+ Ok(Some(ImplSource::Builtin(_)))
+ if trait_ref.self_ty().is_closure()
+ && tcx.fn_trait_kind_from_def_id(trait_id).is_some() =>
+ {
+ let ty::Closure(closure_def_id, substs) =
+ *trait_ref.self_ty().kind()
+ else {
+ unreachable!()
+ };
+ if !tcx.is_const_fn_raw(closure_def_id) {
self.check_op(ops::FnCallNonConst {
caller,
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
@@ -814,7 +815,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
@@ -838,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
tcx,
ObligationCause::dummy_with_span(*fn_span),
param_env,
- poly_trait_pred,
+ trait_ref,
);
// improve diagnostics by showing what failed. Our requirements are stricter this time
@@ -857,7 +858,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
@@ -917,7 +918,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
callee,
substs,
span: *fn_span,
- from_hir_call: *from_hir_call,
+ call_source: *call_source,
feature: None,
});
return;
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 21f3c2c89..4eb278252 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -2,18 +2,16 @@
use hir::def_id::LocalDefId;
use hir::{ConstContext, LangItem};
-use rustc_errors::{
- error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
-};
+use rustc_errors::{error_code, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
-use rustc_middle::mir;
+use rustc_middle::mir::{self, CallSource};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::TraitRef;
use rustc_middle::ty::{suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, Ty};
-use rustc_middle::ty::{Binder, TraitRef};
use rustc_middle::util::{call_kind, CallDesugaringKind, CallKind};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
@@ -102,7 +100,7 @@ pub struct FnCallNonConst<'tcx> {
pub callee: DefId,
pub substs: SubstsRef<'tcx>,
pub span: Span,
- pub from_hir_call: bool,
+ pub call_source: CallSource,
pub feature: Option<Symbol>,
}
@@ -112,7 +110,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
_: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
+ let FnCallNonConst { caller, callee, substs, span, call_source, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
@@ -139,12 +137,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
}
}
Adt(..) => {
- let obligation = Obligation::new(
- tcx,
- ObligationCause::dummy(),
- param_env,
- Binder::dummy(trait_ref),
- );
+ let obligation =
+ Obligation::new(tcx, ObligationCause::dummy(), param_env, trait_ref);
let infcx = tcx.infer_ctxt().build();
let mut selcx = SelectionContext::new(&infcx);
@@ -152,40 +146,45 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
let span = tcx.def_span(data.impl_def_id);
- err.span_note(span, "impl defined here, but it is not `const`");
+ err.subdiagnostic(errors::NonConstImplNote { span });
}
}
_ => {}
}
};
- let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
+ let call_kind =
+ call_kind(tcx, ccx.param_env, callee, substs, span, call_source.from_hir_call(), None);
debug!(?call_kind);
let mut err = match call_kind {
CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
macro_rules! error {
- ($fmt:literal) => {
- struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
+ ($err:ident) => {
+ tcx.sess.create_err(errors::$err {
+ span,
+ ty: self_ty,
+ kind: ccx.const_kind(),
+ })
};
}
let mut err = match kind {
CallDesugaringKind::ForLoopIntoIter => {
- error!("cannot convert `{}` into an iterator in {}s")
+ error!(NonConstForLoopIntoIter)
}
CallDesugaringKind::QuestionBranch => {
- error!("`?` cannot determine the branch of `{}` in {}s")
+ error!(NonConstQuestionBranch)
}
CallDesugaringKind::QuestionFromResidual => {
- error!("`?` cannot convert from residual of `{}` in {}s")
+ error!(NonConstQuestionFromResidual)
}
CallDesugaringKind::TryBlockFromOutput => {
- error!("`try` block cannot convert `{}` to the result in {}s")
+ error!(NonConstTryBlockFromOutput)
}
CallDesugaringKind::Await => {
- error!("cannot convert `{}` into a future in {}s")
+ error!(NonConstAwait)
}
};
@@ -193,108 +192,101 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
err
}
CallKind::FnCall { fn_trait_id, self_ty } => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0015,
- "cannot call non-const closure in {}s",
- ccx.const_kind(),
- );
-
- match self_ty.kind() {
+ let note = match self_ty.kind() {
FnDef(def_id, ..) => {
let span = tcx.def_span(*def_id);
if ccx.tcx.is_const_fn_raw(*def_id) {
span_bug!(span, "calling const FnDef errored when it shouldn't");
}
- err.span_note(span, "function defined here, but it is not `const`");
- }
- FnPtr(..) => {
- err.note(format!(
- "function pointers need an RFC before allowed to be called in {}s",
- ccx.const_kind()
- ));
+ Some(errors::NonConstClosureNote::FnDef { span })
}
- Closure(..) => {
- err.note(format!(
- "closures need an RFC before allowed to be called in {}s",
- ccx.const_kind()
- ));
- }
- _ => {}
- }
+ FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr),
+ Closure(..) => Some(errors::NonConstClosureNote::Closure),
+ _ => None,
+ };
+
+ let mut err = tcx.sess.create_err(errors::NonConstClosure {
+ span,
+ kind: ccx.const_kind(),
+ note,
+ });
diag_trait(&mut err, self_ty, fn_trait_id);
err
}
CallKind::Operator { trait_id, self_ty, .. } => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0015,
- "cannot call non-const operator in {}s",
- ccx.const_kind()
- );
-
- if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
- match (substs[0].unpack(), substs[1].unpack()) {
- (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
- if self_ty == rhs_ty
- && self_ty.is_ref()
- && self_ty.peel_refs().is_primitive() =>
- {
- let mut num_refs = 0;
- let mut tmp_ty = self_ty;
- while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
- num_refs += 1;
- tmp_ty = *inner_ty;
- }
- let deref = "*".repeat(num_refs);
-
- if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
- if let Some(eq_idx) = call_str.find("==") {
- if let Some(rhs_idx) =
- call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
- {
- let rhs_pos =
- span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
- let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
- err.multipart_suggestion(
- "consider dereferencing here",
- vec![
- (span.shrink_to_lo(), deref.clone()),
- (rhs_span, deref),
- ],
- Applicability::MachineApplicable,
- );
+ let mut err = if let CallSource::MatchCmp = call_source {
+ tcx.sess.create_err(errors::NonConstMatchEq {
+ span,
+ kind: ccx.const_kind(),
+ ty: self_ty,
+ })
+ } else {
+ let mut sugg = None;
+
+ if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
+ match (substs[0].unpack(), substs[1].unpack()) {
+ (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+ if self_ty == rhs_ty
+ && self_ty.is_ref()
+ && self_ty.peel_refs().is_primitive() =>
+ {
+ let mut num_refs = 0;
+ let mut tmp_ty = self_ty;
+ while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+ num_refs += 1;
+ tmp_ty = *inner_ty;
+ }
+ let deref = "*".repeat(num_refs);
+
+ if let Ok(call_str) =
+ ccx.tcx.sess.source_map().span_to_snippet(span)
+ {
+ if let Some(eq_idx) = call_str.find("==") {
+ if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
+ .find(|c: char| !c.is_whitespace())
+ {
+ let rhs_pos = span.lo()
+ + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+ let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+ sugg = Some(errors::ConsiderDereferencing {
+ deref,
+ span: span.shrink_to_lo(),
+ rhs_span,
+ });
+ }
}
}
}
+ _ => {}
}
- _ => {}
}
- }
+ tcx.sess.create_err(errors::NonConstOperator {
+ span,
+ kind: ccx.const_kind(),
+ sugg,
+ })
+ };
diag_trait(&mut err, self_ty, trait_id);
err
}
CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0015,
- "cannot perform deref coercion on `{}` in {}s",
- self_ty,
- ccx.const_kind()
- );
-
- err.note(format!("attempting to deref into `{}`", deref_target_ty));
-
// Check first whether the source is accessible (issue #87060)
- if tcx.sess.source_map().is_span_accessible(deref_target) {
- err.span_note(deref_target, "deref defined here");
- }
+ let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
+ Some(deref_target)
+ } else {
+ None
+ };
+
+ let mut err = tcx.sess.create_err(errors::NonConstDerefCoercion {
+ span,
+ ty: self_ty,
+ kind: ccx.const_kind(),
+ target_ty: deref_target_ty,
+ deref_target: target,
+ });
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
err
@@ -432,21 +424,12 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let mut err = struct_span_err!(
- ccx.tcx.sess,
- span,
- E0493,
- "destructor of `{}` cannot be evaluated at compile-time",
- self.dropped_ty,
- );
- err.span_label(
+ ccx.tcx.sess.create_err(errors::LiveDrop {
span,
- format!("the destructor for this type cannot be evaluated in {}s", ccx.const_kind()),
- );
- if let Some(span) = self.dropped_at {
- err.span_label(span, "value is dropped here");
- }
- err
+ dropped_ty: self.dropped_ty,
+ kind: ccx.const_kind(),
+ dropped_at: self.dropped_at,
+ })
}
}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 1da205790..015a4aa94 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -172,7 +172,7 @@ impl Qualif for NeedsNonConstDrop {
if !matches!(
impl_src,
- ImplSource::ConstDestruct(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+ ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
) {
// If our const destruct candidate is not ConstDestruct or implied by the param env,
// then it's bad
@@ -223,7 +223,7 @@ impl Qualif for CustomEq {
def: AdtDef<'tcx>,
substs: SubstsRef<'tcx>,
) -> bool {
- let ty = cx.tcx.mk_adt(def, substs);
+ let ty = Ty::new_adt(cx.tcx, def, substs);
!ty.is_structural_eq_shallow(cx.tcx)
}
}
@@ -344,15 +344,18 @@ where
};
// Check the qualifs of the value of `const` items.
- // FIXME(valtrees): check whether const qualifs should behave the same
- // way for type and mir constants.
let uneval = match constant.literal {
ConstantKind::Ty(ct)
- if matches!(ct.kind(), ty::ConstKind::Param(_) | ty::ConstKind::Error(_)) =>
+ if matches!(
+ ct.kind(),
+ ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
+ ) =>
{
None
}
- ConstantKind::Ty(c) => bug!("expected ConstKind::Param here, found {:?}", c),
+ ConstantKind::Ty(c) => {
+ bug!("expected ConstKind::Param or ConstKind::Value here, found {:?}", c)
+ }
ConstantKind::Unevaluated(uv, _) => Some(uv),
ConstantKind::Val(..) => None,
};
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
index 78c74e189..3a869f7f5 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs
@@ -103,7 +103,7 @@ where
fn ref_allows_mutation(&self, kind: mir::BorrowKind, place: mir::Place<'tcx>) -> bool {
match kind {
mir::BorrowKind::Mut { .. } => true,
- mir::BorrowKind::Shared | mir::BorrowKind::Shallow | mir::BorrowKind::Unique => {
+ mir::BorrowKind::Shared | mir::BorrowKind::Shallow => {
self.shared_borrow_allows_mutation(place)
}
}
@@ -337,7 +337,7 @@ where
Q: Qualif,
{
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -346,7 +346,7 @@ where
}
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -355,7 +355,7 @@ where
}
fn apply_call_return_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs
index 0e2d9ee8f..1b39a76e4 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs
@@ -14,11 +14,10 @@
use rustc_hir as hir;
use rustc_middle::mir;
-use rustc_middle::mir::traversal::ReversePostorderIter;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, List, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use rustc_index::{Idx, IndexSlice, IndexVec};
@@ -53,9 +52,8 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
return;
}
- let mut rpo = traversal::reverse_postorder(body);
let ccx = ConstCx::new(tcx, body);
- let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo);
+ let (mut temps, all_candidates) = collect_temps_and_candidates(&ccx);
let promotable_candidates = validate_candidates(&ccx, &mut temps, &all_candidates);
@@ -166,14 +164,13 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
pub fn collect_temps_and_candidates<'tcx>(
ccx: &ConstCx<'_, 'tcx>,
- rpo: &mut ReversePostorderIter<'_, 'tcx>,
) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
let mut collector = Collector {
temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls),
candidates: vec![],
ccx,
};
- for (bb, data) in rpo {
+ for (bb, data) in traversal::reverse_postorder(ccx.body) {
collector.visit_basic_block_data(bb, data);
}
(collector.temps, collector.candidates)
@@ -457,7 +454,9 @@ impl<'tcx> Validator<'_, 'tcx> {
match kind {
// Reject these borrow types just to be safe.
// FIXME(RalfJung): could we allow them? Should we? No point in it until we have a usecase.
- BorrowKind::Shallow | BorrowKind::Unique => return Err(Unpromotable),
+ BorrowKind::Shallow | BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture } => {
+ return Err(Unpromotable);
+ }
BorrowKind::Shared => {
let has_mut_interior = self.qualif_local::<qualifs::HasMutInterior>(place.local);
@@ -466,7 +465,9 @@ impl<'tcx> Validator<'_, 'tcx> {
}
}
- BorrowKind::Mut { .. } => {
+ // FIXME: consider changing this to only promote &mut [] for default borrows,
+ // also forbidding two phase borrows
+ BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow } => {
let ty = place.ty(self.body, self.tcx).ty;
// In theory, any zero-sized value could be borrowed
@@ -572,13 +573,18 @@ impl<'tcx> Validator<'_, 'tcx> {
| BinOp::Gt
| BinOp::Offset
| BinOp::Add
+ | BinOp::AddUnchecked
| BinOp::Sub
+ | BinOp::SubUnchecked
| BinOp::Mul
+ | BinOp::MulUnchecked
| BinOp::BitXor
| BinOp::BitAnd
| BinOp::BitOr
| BinOp::Shl
- | BinOp::Shr => {}
+ | BinOp::ShlUnchecked
+ | BinOp::Shr
+ | BinOp::ShrUnchecked => {}
}
self.validate_operand(lhs)?;
@@ -795,7 +801,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
};
match terminator.kind {
- TerminatorKind::Call { mut func, mut args, from_hir_call, fn_span, .. } => {
+ TerminatorKind::Call {
+ mut func, mut args, call_source: desugar, fn_span, ..
+ } => {
self.visit_operand(&mut func, loc);
for arg in &mut args {
self.visit_operand(arg, loc);
@@ -811,7 +819,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
unwind: UnwindAction::Continue,
destination: Place::from(new_temp),
target: Some(new_target),
- from_hir_call,
+ call_source: desugar,
fn_span,
},
source_info: SourceInfo::outermost(terminator.source_info.span),
@@ -859,7 +867,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
let ty = local_decls[place.local].ty;
let span = statement.source_info.span;
- let ref_ty = tcx.mk_ref(
+ let ref_ty = Ty::new_ref(
+ tcx,
tcx.lifetimes.re_erased,
ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
);
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 3c350e25b..4cc923cd9 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -67,8 +67,8 @@ impl<'tcx> MirPass<'tcx> for Validator {
unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
- place_cache: Vec::new(),
- value_cache: Vec::new(),
+ place_cache: FxHashSet::default(),
+ value_cache: FxHashSet::default(),
};
checker.visit_body(body);
checker.check_cleanup_control_flow();
@@ -95,8 +95,8 @@ struct TypeChecker<'a, 'tcx> {
unwind_edge_count: usize,
reachable_blocks: BitSet<BasicBlock>,
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
- place_cache: Vec<PlaceRef<'tcx>>,
- value_cache: Vec<u128>,
+ place_cache: FxHashSet<PlaceRef<'tcx>>,
+ value_cache: FxHashSet<u128>,
}
impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -318,8 +318,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_projection_elem(
&mut self,
- local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
@@ -334,7 +333,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ProjectionElem::Deref
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
{
- let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
+ let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty;
if base_ty.is_box() {
self.fail(
@@ -344,8 +343,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
ProjectionElem::Field(f, ty) => {
- let parent = Place { local, projection: self.tcx.mk_place_elems(proj_base) };
- let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
+ let parent_ty = place_ref.ty(&self.body.local_decls, self.tcx);
let fail_out_of_bounds = |this: &Self, location| {
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
};
@@ -355,7 +353,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
location,
format!(
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
- parent, f, ty, f_ty
+ place_ref, f, ty, f_ty
)
)
}
@@ -434,7 +432,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
_ => {}
}
- self.super_projection_elem(local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
}
fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
@@ -498,8 +496,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
macro_rules! check_kinds {
- ($t:expr, $text:literal, $($patterns:tt)*) => {
- if !matches!(($t).kind(), $($patterns)*) {
+ ($t:expr, $text:literal, $typat:pat) => {
+ if !matches!(($t).kind(), $typat) {
self.fail(location, format!($text, $t));
}
};
@@ -527,6 +525,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
use BinOp::*;
let a = vals.0.ty(&self.body.local_decls, self.tcx);
let b = vals.1.ty(&self.body.local_decls, self.tcx);
+ if crate::util::binop_right_homogeneous(*op) {
+ if let Eq | Lt | Le | Ne | Ge | Gt = op {
+ // The function pointer types can have lifetimes
+ if !self.mir_assign_valid_types(a, b) {
+ self.fail(
+ location,
+ format!("Cannot {op:?} compare incompatible types {a:?} and {b:?}"),
+ );
+ }
+ } else if a != b {
+ self.fail(
+ location,
+ format!(
+ "Cannot perform binary op {op:?} on unequal types {a:?} and {b:?}"
+ ),
+ );
+ }
+ }
+
match op {
Offset => {
check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
@@ -538,7 +555,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
for x in [a, b] {
check_kinds!(
x,
- "Cannot compare type {:?}",
+ "Cannot {op:?} compare type {:?}",
ty::Bool
| ty::Char
| ty::Int(..)
@@ -548,19 +565,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
| ty::FnPtr(..)
)
}
- // The function pointer types can have lifetimes
- if !self.mir_assign_valid_types(a, b) {
- self.fail(
- location,
- format!("Cannot compare unequal types {:?} and {:?}", a, b),
- );
- }
}
- Shl | Shr => {
+ AddUnchecked | SubUnchecked | MulUnchecked | Shl | ShlUnchecked | Shr
+ | ShrUnchecked => {
for x in [a, b] {
check_kinds!(
x,
- "Cannot shift non-integer type {:?}",
+ "Cannot {op:?} non-integer type {:?}",
ty::Uint(..) | ty::Int(..)
)
}
@@ -569,37 +580,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
for x in [a, b] {
check_kinds!(
x,
- "Cannot perform bitwise op on type {:?}",
+ "Cannot perform bitwise op {op:?} on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Bool
)
}
- if a != b {
- self.fail(
- location,
- format!(
- "Cannot perform bitwise op on unequal types {:?} and {:?}",
- a, b
- ),
- );
- }
}
Add | Sub | Mul | Div | Rem => {
for x in [a, b] {
check_kinds!(
x,
- "Cannot perform arithmetic on type {:?}",
+ "Cannot perform arithmetic {op:?} on type {:?}",
ty::Uint(..) | ty::Int(..) | ty::Float(..)
)
}
- if a != b {
- self.fail(
- location,
- format!(
- "Cannot perform arithmetic on unequal types {:?} and {:?}",
- a, b
- ),
- );
- }
}
}
}
@@ -657,7 +650,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// FIXME: Add Checks for these
CastKind::PointerFromExposedAddress
| CastKind::PointerExposeAddress
- | CastKind::Pointer(_) => {}
+ | CastKind::PointerCoercion(_) => {}
CastKind::IntToInt | CastKind::IntToFloat => {
let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();
let target_valid = target_type.is_numeric() || target_type.is_char();
@@ -958,10 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.value_cache.clear();
self.value_cache.extend(targets.iter().map(|(value, _)| value));
- let all_len = self.value_cache.len();
- self.value_cache.sort_unstable();
- self.value_cache.dedup();
- let has_duplicates = all_len != self.value_cache.len();
+ let has_duplicates = targets.iter().len() != self.value_cache.len();
if has_duplicates {
self.fail(
location,
@@ -994,16 +984,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// passed by a reference to the callee. Consequently they must be non-overlapping.
// Currently this simply checks for duplicate places.
self.place_cache.clear();
- self.place_cache.push(destination.as_ref());
+ self.place_cache.insert(destination.as_ref());
+ let mut has_duplicates = false;
for arg in args {
if let Operand::Move(place) = arg {
- self.place_cache.push(place.as_ref());
+ has_duplicates |= !self.place_cache.insert(place.as_ref());
}
}
- let all_len = self.place_cache.len();
- let mut dedup = FxHashSet::default();
- self.place_cache.retain(|p| dedup.insert(*p));
- let has_duplicates = all_len != self.place_cache.len();
+
if has_duplicates {
self.fail(
location,
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index 23fcd22c5..2d1970791 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -1,9 +1,8 @@
use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
-use rustc_session::Limit;
use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
-use crate::const_eval::{CheckAlignment, CompileTimeInterpreter};
+use crate::const_eval::{CanAccessStatics, CheckAlignment, CompileTimeInterpreter};
use crate::interpret::{InterpCx, MemoryKind, OpTy};
/// Determines if this type permits "raw" initialization by just transmuting some memory into an
@@ -22,7 +21,7 @@ pub fn check_validity_requirement<'tcx>(
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
param_env_and_ty: ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> Result<bool, LayoutError<'tcx>> {
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
let layout = tcx.layout_of(param_env_and_ty)?;
// There is nothing strict or lax about inhabitedness.
@@ -44,12 +43,8 @@ fn might_permit_raw_init_strict<'tcx>(
ty: TyAndLayout<'tcx>,
tcx: TyCtxt<'tcx>,
kind: ValidityRequirement,
-) -> Result<bool, LayoutError<'tcx>> {
- let machine = CompileTimeInterpreter::new(
- Limit::new(0),
- /*can_access_statics:*/ false,
- CheckAlignment::Error,
- );
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
+ let machine = CompileTimeInterpreter::new(CanAccessStatics::No, CheckAlignment::Error);
let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
@@ -80,7 +75,7 @@ fn might_permit_raw_init_lax<'tcx>(
this: TyAndLayout<'tcx>,
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
init_kind: ValidityRequirement,
-) -> Result<bool, LayoutError<'tcx>> {
+) -> Result<bool, &'tcx LayoutError<'tcx>> {
let scalar_allows_raw_init = move |s: Scalar| -> bool {
match init_kind {
ValidityRequirement::Inhabited => {
diff --git a/compiler/rustc_const_eval/src/util/mod.rs b/compiler/rustc_const_eval/src/util/mod.rs
index 7641f5607..289e34225 100644
--- a/compiler/rustc_const_eval/src/util/mod.rs
+++ b/compiler/rustc_const_eval/src/util/mod.rs
@@ -1,3 +1,5 @@
+use rustc_middle::mir;
+
mod alignment;
mod check_validity_requirement;
mod compare_types;
@@ -7,3 +9,27 @@ pub use self::alignment::is_disaligned;
pub use self::check_validity_requirement::check_validity_requirement;
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
pub use self::type_name::type_name;
+
+/// Classify whether an operator is "left-homogeneous", i.e., the LHS has the
+/// same type as the result.
+#[inline]
+pub(crate) fn binop_left_homogeneous(op: mir::BinOp) -> bool {
+ use rustc_middle::mir::BinOp::*;
+ match op {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+ | BitAnd | BitOr | Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => true,
+ Eq | Ne | Lt | Le | Gt | Ge => false,
+ }
+}
+
+/// Classify whether an operator is "right-homogeneous", i.e., the RHS has the
+/// same type as the LHS.
+#[inline]
+pub(crate) fn binop_right_homogeneous(op: mir::BinOp) -> bool {
+ use rustc_middle::mir::BinOp::*;
+ match op {
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+ | BitAnd | BitOr | Eq | Ne | Lt | Le | Gt | Ge => true,
+ Offset | Shl | ShlUnchecked | Shr | ShrUnchecked => false,
+ }
+}
diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs
index 11ad5b49d..4f01e0a24 100644
--- a/compiler/rustc_const_eval/src/util/type_name.rs
+++ b/compiler/rustc_const_eval/src/util/type_name.rs
@@ -63,6 +63,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
| ty::Generator(def_id, substs, _) => self.print_def_path(def_id, substs),
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
+ ty::Alias(ty::Weak, _) => bug!("type_name: unexpected weak projection"),
ty::Alias(ty::Inherent, _) => bug!("type_name: unexpected inherent projection"),
ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"),
ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"),
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 78f73d193..a5c3cb3f8 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -10,7 +10,7 @@ arrayvec = { version = "0.7", default-features = false }
bitflags = "1.2.1"
cfg-if = "1.0"
ena = "0.14.2"
-indexmap = { version = "1.9.3" }
+indexmap = { version = "2.0.0" }
jobserver_crate = { version = "0.1.13", package = "jobserver" }
libc = "0.2"
measureme = "10.0.0"
diff --git a/compiler/rustc_data_structures/src/intern.rs b/compiler/rustc_data_structures/src/intern.rs
index ba94f3776..e0f8c350c 100644
--- a/compiler/rustc_data_structures/src/intern.rs
+++ b/compiler/rustc_data_structures/src/intern.rs
@@ -1,5 +1,6 @@
use crate::stable_hasher::{HashStable, StableHasher};
use std::cmp::Ordering;
+use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::ptr;
@@ -20,7 +21,6 @@ mod private {
/// The `PrivateZst` field means you can pattern match with `Interned(v, _)`
/// but you can only construct a `Interned` with `new_unchecked`, and not
/// directly.
-#[derive(Debug)]
#[rustc_pass_by_value]
pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst);
@@ -108,5 +108,11 @@ where
}
}
+impl<T: Debug> Debug for Interned<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
#[cfg(test)]
mod tests;
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 859e384d8..3deb9c5c2 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -68,7 +68,6 @@ pub mod macros;
pub mod obligation_forest;
pub mod sip128;
pub mod small_c_str;
-pub mod small_str;
pub mod snapshot_map;
pub mod svh;
pub use ena::snapshot_vec;
diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs
index 7ed70ba1e..40cbf1495 100644
--- a/compiler/rustc_data_structures/src/sharded.rs
+++ b/compiler/rustc_data_structures/src/sharded.rs
@@ -1,4 +1,6 @@
use crate::fx::{FxHashMap, FxHasher};
+#[cfg(parallel_compiler)]
+use crate::sync::is_dyn_thread_safe;
use crate::sync::{CacheAligned, Lock, LockGuard};
use std::borrow::Borrow;
use std::collections::hash_map::RawEntryMut;
@@ -18,6 +20,11 @@ pub const SHARDS: usize = 1 << SHARD_BITS;
/// An array of cache-line aligned inner locked structures with convenience methods.
pub struct Sharded<T> {
+ /// This mask is used to ensure that accesses are inbounds of `shards`.
+ /// When dynamic thread safety is off, this field is set to 0 causing only
+ /// a single shard to be used for greater cache efficiency.
+ #[cfg(parallel_compiler)]
+ mask: usize,
shards: [CacheAligned<Lock<T>>; SHARDS],
}
@@ -31,31 +38,54 @@ impl<T: Default> Default for Sharded<T> {
impl<T> Sharded<T> {
#[inline]
pub fn new(mut value: impl FnMut() -> T) -> Self {
- Sharded { shards: [(); SHARDS].map(|()| CacheAligned(Lock::new(value()))) }
+ Sharded {
+ #[cfg(parallel_compiler)]
+ mask: if is_dyn_thread_safe() { SHARDS - 1 } else { 0 },
+ shards: [(); SHARDS].map(|()| CacheAligned(Lock::new(value()))),
+ }
+ }
+
+ #[inline(always)]
+ fn mask(&self) -> usize {
+ #[cfg(parallel_compiler)]
+ {
+ if SHARDS == 1 { 0 } else { self.mask }
+ }
+ #[cfg(not(parallel_compiler))]
+ {
+ 0
+ }
+ }
+
+ #[inline(always)]
+ fn count(&self) -> usize {
+ // `self.mask` is always one below the used shard count
+ self.mask() + 1
}
/// The shard is selected by hashing `val` with `FxHasher`.
#[inline]
pub fn get_shard_by_value<K: Hash + ?Sized>(&self, val: &K) -> &Lock<T> {
- if SHARDS == 1 { &self.shards[0].0 } else { self.get_shard_by_hash(make_hash(val)) }
+ self.get_shard_by_hash(if SHARDS == 1 { 0 } else { make_hash(val) })
}
#[inline]
pub fn get_shard_by_hash(&self, hash: u64) -> &Lock<T> {
- &self.shards[get_shard_index_by_hash(hash)].0
+ self.get_shard_by_index(get_shard_hash(hash))
}
#[inline]
pub fn get_shard_by_index(&self, i: usize) -> &Lock<T> {
- &self.shards[i].0
+ // SAFETY: The index get ANDed with the mask, ensuring it is always inbounds.
+ unsafe { &self.shards.get_unchecked(i & self.mask()).0 }
}
pub fn lock_shards(&self) -> Vec<LockGuard<'_, T>> {
- (0..SHARDS).map(|i| self.shards[i].0.lock()).collect()
+ (0..self.count()).map(|i| self.get_shard_by_index(i).lock()).collect()
}
pub fn try_lock_shards(&self) -> Option<Vec<LockGuard<'_, T>>> {
- (0..SHARDS).map(|i| self.shards[i].0.try_lock()).collect()
+ (0..self.count()).map(|i| self.get_shard_by_index(i).try_lock()).collect()
}
}
@@ -136,11 +166,9 @@ pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
/// `hash` can be computed with any hasher, so long as that hasher is used
/// consistently for each `Sharded` instance.
#[inline]
-#[allow(clippy::modulo_one)]
-pub fn get_shard_index_by_hash(hash: u64) -> usize {
+fn get_shard_hash(hash: u64) -> usize {
let hash_len = mem::size_of::<usize>();
// Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.
// hashbrown also uses the lowest bits, so we can't use those
- let bits = (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize;
- bits % SHARDS
+ (hash >> (hash_len * 8 - 7 - SHARD_BITS)) as usize
}
diff --git a/compiler/rustc_data_structures/src/small_str.rs b/compiler/rustc_data_structures/src/small_str.rs
deleted file mode 100644
index 800acb1b0..000000000
--- a/compiler/rustc_data_structures/src/small_str.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use smallvec::SmallVec;
-
-#[cfg(test)]
-mod tests;
-
-/// Like SmallVec but for strings.
-#[derive(Default)]
-pub struct SmallStr<const N: usize>(SmallVec<[u8; N]>);
-
-impl<const N: usize> SmallStr<N> {
- #[inline]
- pub fn new() -> Self {
- SmallStr(SmallVec::default())
- }
-
- #[inline]
- pub fn push_str(&mut self, s: &str) {
- self.0.extend_from_slice(s.as_bytes());
- }
-
- #[inline]
- pub fn empty(&self) -> bool {
- self.0.is_empty()
- }
-
- #[inline]
- pub fn spilled(&self) -> bool {
- self.0.spilled()
- }
-
- #[inline]
- pub fn as_str(&self) -> &str {
- unsafe { std::str::from_utf8_unchecked(self.0.as_slice()) }
- }
-}
-
-impl<const N: usize> std::ops::Deref for SmallStr<N> {
- type Target = str;
-
- #[inline]
- fn deref(&self) -> &str {
- self.as_str()
- }
-}
-
-impl<const N: usize, A: AsRef<str>> FromIterator<A> for SmallStr<N> {
- #[inline]
- fn from_iter<T>(iter: T) -> Self
- where
- T: IntoIterator<Item = A>,
- {
- let mut s = SmallStr::default();
- s.extend(iter);
- s
- }
-}
-
-impl<const N: usize, A: AsRef<str>> Extend<A> for SmallStr<N> {
- #[inline]
- fn extend<T>(&mut self, iter: T)
- where
- T: IntoIterator<Item = A>,
- {
- for a in iter.into_iter() {
- self.push_str(a.as_ref());
- }
- }
-}
diff --git a/compiler/rustc_data_structures/src/small_str/tests.rs b/compiler/rustc_data_structures/src/small_str/tests.rs
deleted file mode 100644
index 7635a9b72..000000000
--- a/compiler/rustc_data_structures/src/small_str/tests.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-use super::*;
-
-#[test]
-fn empty() {
- let s = SmallStr::<1>::new();
- assert!(s.empty());
- assert_eq!("", s.as_str());
- assert!(!s.spilled());
-}
-
-#[test]
-fn from_iter() {
- let s = ["aa", "bb", "cc"].iter().collect::<SmallStr<6>>();
- assert_eq!("aabbcc", s.as_str());
- assert!(!s.spilled());
-
- let s = ["aa", "bb", "cc", "dd"].iter().collect::<SmallStr<6>>();
- assert_eq!("aabbccdd", s.as_str());
- assert!(s.spilled());
-}
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index 6d57d81c5..6d75b0fb8 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -1,6 +1,6 @@
use crate::sip128::SipHasher128;
use rustc_index::bit_set::{self, BitSet};
-use rustc_index::{Idx, IndexVec};
+use rustc_index::{Idx, IndexSlice, IndexVec};
use smallvec::SmallVec;
use std::fmt;
use std::hash::{BuildHasher, Hash, Hasher};
@@ -233,7 +233,17 @@ pub trait ToStableHashKey<HCX> {
/// - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete
/// values depend on state that might be different between
/// compilation sessions.
-pub unsafe trait StableOrd: Ord {}
+///
+/// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether
+/// unstable sorting can be used for this type. Set to true if and
+/// only if `a == b` implies `a` and `b` are fully indistinguishable.
+pub unsafe trait StableOrd: Ord {
+ const CAN_USE_UNSTABLE_SORT: bool;
+}
+
+unsafe impl<T: StableOrd> StableOrd for &T {
+ const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
+}
/// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
/// that has the same requirements.
@@ -253,7 +263,9 @@ macro_rules! impl_stable_traits_for_trivial_type {
}
}
- unsafe impl $crate::stable_hasher::StableOrd for $t {}
+ unsafe impl $crate::stable_hasher::StableOrd for $t {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+ }
};
}
@@ -339,6 +351,10 @@ impl<T1: HashStable<CTX>, T2: HashStable<CTX>, CTX> HashStable<CTX> for (T1, T2)
}
}
+unsafe impl<T1: StableOrd, T2: StableOrd> StableOrd for (T1, T2) {
+ const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT;
+}
+
impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
where
T1: HashStable<CTX>,
@@ -353,6 +369,11 @@ where
}
}
+unsafe impl<T1: StableOrd, T2: StableOrd, T3: StableOrd> StableOrd for (T1, T2, T3) {
+ const CAN_USE_UNSTABLE_SORT: bool =
+ T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT;
+}
+
impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
where
T1: HashStable<CTX>,
@@ -369,6 +390,15 @@ where
}
}
+unsafe impl<T1: StableOrd, T2: StableOrd, T3: StableOrd, T4: StableOrd> StableOrd
+ for (T1, T2, T3, T4)
+{
+ const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT
+ && T2::CAN_USE_UNSTABLE_SORT
+ && T3::CAN_USE_UNSTABLE_SORT
+ && T4::CAN_USE_UNSTABLE_SORT;
+}
+
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
default fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.len().hash_stable(ctx, hasher);
@@ -459,6 +489,10 @@ impl<CTX> HashStable<CTX> for str {
}
}
+unsafe impl StableOrd for &str {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
+
impl<CTX> HashStable<CTX> for String {
#[inline]
fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
@@ -468,7 +502,9 @@ impl<CTX> HashStable<CTX> for String {
// Safety: String comparison only depends on their contents and the
// contents are not changed by (de-)serialization.
-unsafe impl StableOrd for String {}
+unsafe impl StableOrd for String {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
impl<HCX> ToStableHashKey<HCX> for String {
type KeyType = String;
@@ -494,7 +530,9 @@ impl<CTX> HashStable<CTX> for bool {
}
// Safety: sort order of bools is not changed by (de-)serialization.
-unsafe impl StableOrd for bool {}
+unsafe impl StableOrd for bool {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
impl<T, CTX> HashStable<CTX> for Option<T>
where
@@ -512,7 +550,9 @@ where
}
// Safety: the Option wrapper does not add instability to comparison.
-unsafe impl<T: StableOrd> StableOrd for Option<T> {}
+unsafe impl<T: StableOrd> StableOrd for Option<T> {
+ const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
+}
impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
where
@@ -557,6 +597,18 @@ where
}
}
+impl<I: Idx, T, CTX> HashStable<CTX> for IndexSlice<I, T>
+where
+ T: HashStable<CTX>,
+{
+ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+ self.len().hash_stable(ctx, hasher);
+ for v in &self.raw {
+ v.hash_stable(ctx, hasher);
+ }
+ }
+}
+
impl<I: Idx, T, CTX> HashStable<CTX> for IndexVec<I, T>
where
T: HashStable<CTX>,
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 6c3197d8e..25a082373 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -139,9 +139,14 @@ cfg_if! {
impl Atomic<bool> {
pub fn fetch_or(&self, val: bool, _: Ordering) -> bool {
- let result = self.0.get() | val;
- self.0.set(val);
- result
+ let old = self.0.get();
+ self.0.set(val | old);
+ old
+ }
+ pub fn fetch_and(&self, val: bool, _: Ordering) -> bool {
+ let old = self.0.get();
+ self.0.set(val & old);
+ old
}
}
diff --git a/compiler/rustc_data_structures/src/temp_dir.rs b/compiler/rustc_data_structures/src/temp_dir.rs
index a780d2386..621d3011a 100644
--- a/compiler/rustc_data_structures/src/temp_dir.rs
+++ b/compiler/rustc_data_structures/src/temp_dir.rs
@@ -16,7 +16,7 @@ impl Drop for MaybeTempDir {
// occur.
let dir = unsafe { ManuallyDrop::take(&mut self.dir) };
if self.keep {
- dir.into_path();
+ let _ = dir.into_path();
}
}
}
diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs
index 6c8d54146..2b21815b6 100644
--- a/compiler/rustc_data_structures/src/unord.rs
+++ b/compiler/rustc_data_structures/src/unord.rs
@@ -107,6 +107,10 @@ impl<T, I: Iterator<Item = T>> UnordItems<T, I> {
{
UnordItems(self.0.flat_map(f))
}
+
+ pub fn collect<C: From<UnordItems<T, I>>>(self) -> C {
+ self.into()
+ }
}
impl<T> UnordItems<T, std::iter::Empty<T>> {
@@ -140,12 +144,12 @@ impl<T: Ord, I: Iterator<Item = T>> UnordItems<T, I> {
}
#[inline]
- pub fn into_sorted_stable_ord(self, use_stable_sort: bool) -> Vec<T>
+ pub fn into_sorted_stable_ord(self) -> Vec<T>
where
T: Ord + StableOrd,
{
let mut items: Vec<T> = self.0.collect();
- if use_stable_sort {
+ if !T::CAN_USE_UNSTABLE_SORT {
items.sort();
} else {
items.sort_unstable()
diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml
index d7c295418..86a54f6be 100644
--- a/compiler/rustc_driver/Cargo.toml
+++ b/compiler/rustc_driver/Cargo.toml
@@ -8,3 +8,6 @@ crate-type = ["dylib"]
[dependencies]
rustc_driver_impl = { path = "../rustc_driver_impl" }
+# FIXME(Nilstrieb): 0.37.12 adds eventfd support for FreeBSD,
+# but FreeBSD 12 does not support it: https://github.com/bytecodealliance/rustix/issues/716
+rustix = "=0.37.11"
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index 4eabba575..0cd0b51b6 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -1,4 +1,4 @@
-// This crate is intentionally empty and a rexport of `rustc_driver_impl` to allow the code in
+// This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in
// `rustc_driver_impl` to be compiled in parallel with other crates.
pub use rustc_driver_impl::*;
diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs
index eb92ccc17..654d7636d 100644
--- a/compiler/rustc_driver_impl/src/args.rs
+++ b/compiler/rustc_driver_impl/src/args.rs
@@ -3,6 +3,8 @@ use std::fmt;
use std::fs;
use std::io;
+use rustc_session::EarlyErrorHandler;
+
fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
if let Some(path) = arg.strip_prefix('@') {
let file = match fs::read_to_string(path) {
@@ -21,15 +23,12 @@ fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
/// **Note:** This function doesn't interpret argument 0 in any special way.
/// If this function is intended to be used with command line arguments,
/// `argv[0]` must be removed prior to calling it manually.
-pub fn arg_expand_all(at_args: &[String]) -> Vec<String> {
+pub fn arg_expand_all(handler: &EarlyErrorHandler, at_args: &[String]) -> Vec<String> {
let mut args = Vec::new();
for arg in at_args {
match arg_expand(arg.clone()) {
Ok(arg) => args.extend(arg),
- Err(err) => rustc_session::early_error(
- rustc_session::config::ErrorOutputType::default(),
- format!("Failed to load argument file: {err}"),
- ),
+ Err(err) => handler.early_error(format!("Failed to load argument file: {err}")),
}
}
args
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 0b5d73709..9352fe314 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -24,6 +24,7 @@ use rustc_data_structures::profiling::{
};
use rustc_data_structures::sync::SeqCst;
use rustc_errors::registry::{InvalidErrorCode, Registry};
+use rustc_errors::{markdown, ColorConfig};
use rustc_errors::{
DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl,
};
@@ -34,12 +35,13 @@ use rustc_interface::{interface, Queries};
use rustc_lint::LintStore;
use rustc_metadata::locator;
use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
-use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
+use rustc_session::config::{
+ ErrorOutputType, Input, OutFileName, OutputType, PrintRequest, TrimmedDefPaths,
+};
use rustc_session::cstore::MetadataLoader;
use rustc_session::getopts::{self, Matches};
use rustc_session::lint::{Lint, LintId};
-use rustc_session::{config, Session};
-use rustc_session::{early_error, early_error_no_abort, early_warn};
+use rustc_session::{config, EarlyErrorHandler, Session};
use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
use rustc_target::json::ToJson;
@@ -58,11 +60,18 @@ use std::str;
use std::sync::OnceLock;
use std::time::Instant;
+#[allow(unused_macros)]
+macro do_not_use_print($($t:tt)*) {
+ std::compile_error!(
+ "Don't use `print` or `println` here, use `safe_print` or `safe_println` instead"
+ )
+}
+
// This import blocks the use of panicking `print` and `println` in all the code
// below. Please use `safe_print` and `safe_println` to avoid ICE when
// encountering an I/O error during print.
#[allow(unused_imports)]
-use std::{compile_error as print, compile_error as println};
+use {do_not_use_print as print, do_not_use_print as println};
pub mod args;
pub mod pretty;
@@ -88,6 +97,7 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[
rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE,
rustc_const_eval::DEFAULT_LOCALE_RESOURCE,
rustc_error_messages::DEFAULT_LOCALE_RESOURCE,
+ rustc_errors::DEFAULT_LOCALE_RESOURCE,
rustc_expand::DEFAULT_LOCALE_RESOURCE,
rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE,
rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE,
@@ -164,6 +174,7 @@ pub trait Callbacks {
/// continue the compilation afterwards (defaults to `Compilation::Continue`)
fn after_analysis<'tcx>(
&mut self,
+ _handler: &EarlyErrorHandler,
_compiler: &interface::Compiler,
_queries: &'tcx Queries<'tcx>,
) -> Compilation {
@@ -250,6 +261,8 @@ fn run_compiler(
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
>,
) -> interface::Result<()> {
+ let mut early_error_handler = EarlyErrorHandler::new(ErrorOutputType::default());
+
// Throw away the first argument, the name of the binary.
// In case of at_args being empty, as might be the case by
// passing empty argument array to execve under some platforms,
@@ -260,22 +273,22 @@ fn run_compiler(
// the compiler with @empty_file as argv[0] and no more arguments.
let at_args = at_args.get(1..).unwrap_or_default();
- let args = args::arg_expand_all(at_args);
+ let args = args::arg_expand_all(&early_error_handler, at_args);
- let Some(matches) = handle_options(&args) else { return Ok(()) };
+ let Some(matches) = handle_options(&early_error_handler, &args) else { return Ok(()) };
- let sopts = config::build_session_options(&matches);
+ let sopts = config::build_session_options(&mut early_error_handler, &matches);
// Set parallel mode before thread pool creation, which will create `Lock`s.
interface::set_thread_safe_mode(&sopts.unstable_opts);
if let Some(ref code) = matches.opt_str("explain") {
- handle_explain(diagnostics_registry(), code, sopts.error_format);
+ handle_explain(&early_error_handler, diagnostics_registry(), code, sopts.color);
return Ok(());
}
- let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
- let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg"));
+ let cfg = interface::parse_cfgspecs(&early_error_handler, matches.opt_strs("cfg"));
+ let check_cfg = interface::parse_check_cfg(&early_error_handler, matches.opt_strs("check-cfg"));
let (odir, ofile) = make_output(&matches);
let mut config = interface::Config {
opts: sopts,
@@ -294,7 +307,7 @@ fn run_compiler(
registry: diagnostics_registry(),
};
- match make_input(config.opts.error_format, &matches.free) {
+ match make_input(&early_error_handler, &matches.free) {
Err(reported) => return Err(reported),
Ok(Some(input)) => {
config.input = input;
@@ -304,8 +317,13 @@ fn run_compiler(
Ok(None) => match matches.free.len() {
0 => {
callbacks.config(&mut config);
+
+ early_error_handler.abort_if_errors();
+
interface::run_compiler(config, |compiler| {
let sopts = &compiler.session().opts;
+ let handler = EarlyErrorHandler::new(sopts.error_format);
+
if sopts.describe_lints {
let mut lint_store =
rustc_lint::new_lint_store(compiler.session().enable_internal_lints());
@@ -319,31 +337,38 @@ fn run_compiler(
describe_lints(compiler.session(), &lint_store, registered_lints);
return;
}
- let should_stop =
- print_crate_info(&***compiler.codegen_backend(), compiler.session(), false);
+ let should_stop = print_crate_info(
+ &handler,
+ &**compiler.codegen_backend(),
+ compiler.session(),
+ false,
+ );
if should_stop == Compilation::Stop {
return;
}
- early_error(sopts.error_format, "no input filename given")
+ handler.early_error("no input filename given")
});
return Ok(());
}
1 => panic!("make_input should have provided valid inputs"),
- _ => early_error(
- config.opts.error_format,
- format!(
- "multiple input filenames provided (first two filenames are `{}` and `{}`)",
- matches.free[0], matches.free[1],
- ),
- ),
+ _ => early_error_handler.early_error(format!(
+ "multiple input filenames provided (first two filenames are `{}` and `{}`)",
+ matches.free[0], matches.free[1],
+ )),
},
};
+ early_error_handler.abort_if_errors();
+
interface::run_compiler(config, |compiler| {
let sess = compiler.session();
- let should_stop = print_crate_info(&***compiler.codegen_backend(), sess, true)
- .and_then(|| list_metadata(sess, &*compiler.codegen_backend().metadata_loader()))
+ let handler = EarlyErrorHandler::new(sess.opts.error_format);
+
+ let should_stop = print_crate_info(&handler, &**compiler.codegen_backend(), sess, true)
+ .and_then(|| {
+ list_metadata(&handler, sess, &*compiler.codegen_backend().metadata_loader())
+ })
.and_then(|| try_process_rlink(sess, compiler));
if should_stop == Compilation::Stop {
@@ -411,17 +436,24 @@ fn run_compiler(
queries.global_ctxt()?.enter(|tcx| tcx.analysis(()))?;
- if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
+ if callbacks.after_analysis(&handler, compiler, queries) == Compilation::Stop {
return early_exit();
}
- queries.ongoing_codegen()?;
+ let ongoing_codegen = queries.ongoing_codegen()?;
if sess.opts.unstable_opts.print_type_sizes {
sess.code_stats.print_type_sizes();
}
- let linker = queries.linker()?;
+ if sess.opts.unstable_opts.print_vtable_sizes {
+ let crate_name =
+ compiler.session().opts.crate_name.as_deref().unwrap_or("<UNKNOWN_CRATE>");
+
+ sess.code_stats.print_vtable_sizes(crate_name);
+ }
+
+ let linker = queries.linker(ongoing_codegen)?;
Ok(Some(linker))
})?;
@@ -447,15 +479,18 @@ fn run_compiler(
}
// Extract output directory and file from matches.
-fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
+fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<OutFileName>) {
let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
- let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
+ let ofile = matches.opt_str("o").map(|o| match o.as_str() {
+ "-" => OutFileName::Stdout,
+ path => OutFileName::Real(PathBuf::from(path)),
+ });
(odir, ofile)
}
// Extract input (string or file and optional path) from matches.
fn make_input(
- error_format: ErrorOutputType,
+ handler: &EarlyErrorHandler,
free_matches: &[String],
) -> Result<Option<Input>, ErrorGuaranteed> {
if free_matches.len() == 1 {
@@ -465,8 +500,7 @@ fn make_input(
if io::stdin().read_to_string(&mut src).is_err() {
// Immediately stop compilation if there was an issue reading
// the input (for example if the input stream is not UTF-8).
- let reported = early_error_no_abort(
- error_format,
+ let reported = handler.early_error_no_abort(
"couldn't read from stdin, as it did not contain valid UTF-8",
);
return Err(reported);
@@ -507,7 +541,7 @@ impl Compilation {
}
}
-fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
+fn handle_explain(handler: &EarlyErrorHandler, registry: Registry, code: &str, color: ColorConfig) {
let upper_cased_code = code.to_ascii_uppercase();
let normalised =
if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
@@ -531,45 +565,83 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
text.push('\n');
}
if io::stdout().is_terminal() {
- show_content_with_pager(&text);
+ show_md_content_with_pager(&text, color);
} else {
safe_print!("{text}");
}
}
Err(InvalidErrorCode) => {
- early_error(output, format!("{code} is not a valid error code"));
+ handler.early_error(format!("{code} is not a valid error code"));
}
}
}
-fn show_content_with_pager(content: &str) {
+/// If color is always or auto, print formatted & colorized markdown. If color is never or
+/// if formatted printing fails, print the raw text.
+///
+/// Prefers a pager, falls back standard print
+fn show_md_content_with_pager(content: &str, color: ColorConfig) {
+ let mut fallback_to_println = false;
let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
});
- let mut fallback_to_println = false;
+ let mut cmd = Command::new(&pager_name);
+ // FIXME: find if other pagers accept color options
+ let mut print_formatted = if pager_name == "less" {
+ cmd.arg("-r");
+ true
+ } else if ["bat", "catbat", "delta"].iter().any(|v| *v == pager_name) {
+ true
+ } else {
+ false
+ };
- match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
- Ok(mut pager) => {
- if let Some(pipe) = pager.stdin.as_mut() {
- if pipe.write_all(content.as_bytes()).is_err() {
- fallback_to_println = true;
- }
- }
+ if color == ColorConfig::Never {
+ print_formatted = false;
+ } else if color == ColorConfig::Always {
+ print_formatted = true;
+ }
+
+ let mdstream = markdown::MdStream::parse_str(content);
+ let bufwtr = markdown::create_stdout_bufwtr();
+ let mut mdbuf = bufwtr.buffer();
+ if mdstream.write_termcolor_buf(&mut mdbuf).is_err() {
+ print_formatted = false;
+ }
- if pager.wait().is_err() {
+ if let Ok(mut pager) = cmd.stdin(Stdio::piped()).spawn() {
+ if let Some(pipe) = pager.stdin.as_mut() {
+ let res = if print_formatted {
+ pipe.write_all(mdbuf.as_slice())
+ } else {
+ pipe.write_all(content.as_bytes())
+ };
+
+ if res.is_err() {
fallback_to_println = true;
}
}
- Err(_) => {
+
+ if pager.wait().is_err() {
fallback_to_println = true;
}
+ } else {
+ fallback_to_println = true;
}
// If pager fails for whatever reason, we should still print the content
// to standard output
if fallback_to_println {
- safe_print!("{content}");
+ let fmt_success = match color {
+ ColorConfig::Auto => io::stdout().is_terminal() && bufwtr.print(&mdbuf).is_ok(),
+ ColorConfig::Always => bufwtr.print(&mdbuf).is_ok(),
+ ColorConfig::Never => false,
+ };
+
+ if !fmt_success {
+ safe_print!("{content}");
+ }
}
}
@@ -616,7 +688,11 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
}
}
-pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation {
+pub fn list_metadata(
+ handler: &EarlyErrorHandler,
+ sess: &Session,
+ metadata_loader: &dyn MetadataLoader,
+) -> Compilation {
if sess.opts.unstable_opts.ls {
match sess.io.input {
Input::File(ref ifile) => {
@@ -626,7 +702,7 @@ pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Co
safe_println!("{}", String::from_utf8(v).unwrap());
}
Input::Str { .. } => {
- early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
+ handler.early_error("cannot list metadata for stdin");
}
}
return Compilation::Stop;
@@ -636,6 +712,7 @@ pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Co
}
fn print_crate_info(
+ handler: &EarlyErrorHandler,
codegen_backend: &dyn CodegenBackend,
sess: &Session,
parse_attrs: bool,
@@ -695,7 +772,7 @@ fn print_crate_info(
for &style in &crate_types {
let fname =
rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
- safe_println!("{}", fname.file_name().unwrap().to_string_lossy());
+ safe_println!("{}", fname.as_path().file_name().unwrap().to_string_lossy());
}
}
Cfg => {
@@ -752,9 +829,7 @@ fn print_crate_info(
use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
for split in &[Off, Packed, Unpacked] {
- let stable = sess.target.options.supported_split_debuginfo.contains(split);
- let unstable_ok = sess.unstable_options();
- if stable || unstable_ok {
+ if sess.target.options.supported_split_debuginfo.contains(split) {
safe_println!("{split}");
}
}
@@ -769,10 +844,8 @@ fn print_crate_info(
.expect("unknown Apple target OS")
)
} else {
- early_error(
- ErrorOutputType::default(),
- "only Apple targets currently support deployment version info",
- )
+ handler
+ .early_error("only Apple targets currently support deployment version info")
}
}
}
@@ -783,11 +856,12 @@ fn print_crate_info(
/// Prints version information
///
/// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
-pub macro version($binary: literal, $matches: expr) {
+pub macro version($handler: expr, $binary: literal, $matches: expr) {
fn unw(x: Option<&str>) -> &str {
x.unwrap_or("unknown")
}
$crate::version_at_macro_invocation(
+ $handler,
$binary,
$matches,
unw(option_env!("CFG_VERSION")),
@@ -799,6 +873,7 @@ pub macro version($binary: literal, $matches: expr) {
#[doc(hidden)] // use the macro instead
pub fn version_at_macro_invocation(
+ handler: &EarlyErrorHandler,
binary: &str,
matches: &getopts::Matches,
version: &str,
@@ -819,7 +894,7 @@ pub fn version_at_macro_invocation(
let debug_flags = matches.opt_strs("Z");
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
- get_codegen_backend(&None, backend_name).print_version();
+ get_codegen_backend(handler, &None, backend_name).print_version();
}
}
@@ -996,7 +1071,7 @@ Available lint options:
/// Show help for flag categories shared between rustdoc and rustc.
///
/// Returns whether a help option was printed.
-pub fn describe_flag_categories(matches: &Matches) -> bool {
+pub fn describe_flag_categories(handler: &EarlyErrorHandler, matches: &Matches) -> bool {
// Handle the special case of -Wall.
let wall = matches.opt_strs("W");
if wall.iter().any(|x| *x == "all") {
@@ -1018,15 +1093,12 @@ pub fn describe_flag_categories(matches: &Matches) -> bool {
}
if cg_flags.iter().any(|x| *x == "no-stack-check") {
- early_warn(
- ErrorOutputType::default(),
- "the --no-stack-check flag is deprecated and does nothing",
- );
+ handler.early_warn("the --no-stack-check flag is deprecated and does nothing");
}
if cg_flags.iter().any(|x| *x == "passes=list") {
let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
- get_codegen_backend(&None, backend_name).print_passes();
+ get_codegen_backend(handler, &None, backend_name).print_passes();
return true;
}
@@ -1083,7 +1155,7 @@ fn print_flag_list<T>(
///
/// So with all that in mind, the comments below have some more detail about the
/// contortions done here to get things to work out correctly.
-pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
+pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
if args.is_empty() {
// user did not write `-v` nor `-Z unstable-options`, so do not
// include that extra information.
@@ -1109,7 +1181,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
.map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
_ => None,
};
- early_error(ErrorOutputType::default(), msg.unwrap_or_else(|| e.to_string()));
+ handler.early_error(msg.unwrap_or_else(|| e.to_string()));
});
// For all options we just parsed, we check a few aspects:
@@ -1123,7 +1195,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
// we're good to go.
// * Otherwise, if we're an unstable option then we generate an error
// (unstable option being used on stable)
- nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
+ nightly_options::check_nightly_options(handler, &matches, &config::rustc_optgroups());
if matches.opt_present("h") || matches.opt_present("help") {
// Only show unstable options in --help if we accept unstable options.
@@ -1133,12 +1205,12 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
return None;
}
- if describe_flag_categories(&matches) {
+ if describe_flag_categories(handler, &matches) {
return None;
}
if matches.opt_present("version") {
- version!("rustc", &matches);
+ version!(handler, "rustc", &matches);
return None;
}
@@ -1258,7 +1330,8 @@ pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&Handler))
if let Some(msg) = info.payload().downcast_ref::<String>() {
if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)") {
// the error code is already going to be reported when the panic unwinds up the stack
- let _ = early_error_no_abort(ErrorOutputType::default(), msg.as_str());
+ let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let _ = handler.early_error_no_abort(msg.clone());
return;
}
};
@@ -1341,16 +1414,16 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info:
/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version.
-pub fn init_rustc_env_logger() {
- init_env_logger("RUSTC_LOG");
+pub fn init_rustc_env_logger(handler: &EarlyErrorHandler) {
+ init_env_logger(handler, "RUSTC_LOG");
}
/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
/// other than `RUSTC_LOG`.
-pub fn init_env_logger(env: &str) {
+pub fn init_env_logger(handler: &EarlyErrorHandler, env: &str) {
if let Err(error) = rustc_log::init_env_logger(env) {
- early_error(ErrorOutputType::default(), error.to_string());
+ handler.early_error(error.to_string());
}
}
@@ -1406,7 +1479,10 @@ mod signal_handler {
pub fn main() -> ! {
let start_time = Instant::now();
let start_rss = get_resident_set_size();
- init_rustc_env_logger();
+
+ let handler = EarlyErrorHandler::new(ErrorOutputType::default());
+
+ init_rustc_env_logger(&handler);
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ());
@@ -1415,10 +1491,7 @@ pub fn main() -> ! {
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
- early_error(
- ErrorOutputType::default(),
- format!("argument {i} is not valid Unicode: {arg:?}"),
- )
+ handler.early_error(format!("argument {i} is not valid Unicode: {arg:?}"))
})
})
.collect::<Vec<_>>();
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index ee64b18d3..24a5f4030 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -9,7 +9,7 @@ use rustc_hir_pretty as pprust_hir;
use rustc_middle::hir::map as hir_map;
use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
+use rustc_session::config::{OutFileName, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::FileName;
@@ -359,8 +359,8 @@ fn get_source(sess: &Session) -> (String, FileName) {
fn write_or_print(out: &str, sess: &Session) {
match &sess.io.output_file {
- None => print!("{out}"),
- Some(p) => {
+ None | Some(OutFileName::Stdout) => print!("{out}"),
+ Some(OutFileName::Real(p)) => {
if let Err(e) = std::fs::write(p, out) {
sess.emit_fatal(UnprettyDumpFail {
path: p.display().to_string(),
diff --git a/compiler/rustc_error_codes/src/error_codes/E0133.md b/compiler/rustc_error_codes/src/error_codes/E0133.md
index 1adbcc313..8ca3f03ce 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0133.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0133.md
@@ -1,4 +1,4 @@
-Unsafe code was used outside of an unsafe function or block.
+Unsafe code was used outside of an unsafe block.
Erroneous code example:
@@ -30,4 +30,21 @@ fn main() {
See the [unsafe section][unsafe-section] of the Book for more details.
+#### Unsafe code in functions
+
+Unsafe code is currently accepted in unsafe functions, but that is being phased
+out in favor of requiring unsafe blocks here too.
+
+```
+unsafe fn f() { return; }
+
+unsafe fn g() {
+ f(); // Is accepted, but no longer recommended
+ unsafe { f(); } // Recommended way to write this
+}
+```
+
+Linting against this is controlled via the `unsafe_op_in_unsafe_fn` lint, which
+is `allow` by default but will be upgraded to `warn` in a future edition.
+
[unsafe-section]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
diff --git a/compiler/rustc_error_codes/src/error_codes/E0741.md b/compiler/rustc_error_codes/src/error_codes/E0741.md
index 70d963cd4..0c7010526 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0741.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0741.md
@@ -10,15 +10,19 @@ struct A;
struct B<const X: A>; // error!
```
-Only structural-match types (that is, types that derive `PartialEq` and `Eq`)
-may be used as the types of const generic parameters.
+Only structural-match types, which are types that derive `PartialEq` and `Eq`
+and implement `ConstParamTy`, may be used as the types of const generic
+parameters.
-To fix the previous code example, we derive `PartialEq` and `Eq`:
+To fix the previous code example, we derive `PartialEq`, `Eq`, and
+`ConstParamTy`:
```
#![feature(adt_const_params)]
-#[derive(PartialEq, Eq)] // We derive both traits here.
+use std::marker::ConstParamTy;
+
+#[derive(PartialEq, Eq, ConstParamTy)] // We derive both traits here.
struct A;
struct B<const X: A>; // ok!
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 0accb4ab9..51e1fe531 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -226,7 +226,7 @@ fn register_functions(bundle: &mut FluentBundle) {
pub type LazyFallbackBundle = Lrc<Lazy<FluentBundle, impl FnOnce() -> FluentBundle>>;
/// Return the default `FluentBundle` with standard "en-US" diagnostic messages.
-#[instrument(level = "trace")]
+#[instrument(level = "trace", skip(resources))]
pub fn fallback_fluent_bundle(
resources: Vec<&'static str>,
with_directionality_markers: bool,
@@ -242,7 +242,6 @@ pub fn fallback_fluent_bundle(
for resource in resources {
let resource = FluentResource::try_new(resource.to_string())
.expect("failed to parse fallback fluent resource");
- trace!(?resource);
fallback_bundle.add_resource_overriding(resource);
}
@@ -263,8 +262,7 @@ type FluentId = Cow<'static, str>;
#[rustc_diagnostic_item = "SubdiagnosticMessage"]
pub enum SubdiagnosticMessage {
/// Non-translatable diagnostic message.
- // FIXME(davidtwco): can a `Cow<'static, str>` be used here?
- Str(String),
+ Str(Cow<'static, str>),
/// Translatable message which has already been translated eagerly.
///
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
@@ -275,8 +273,7 @@ pub enum SubdiagnosticMessage {
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
/// stores messages which have been translated eagerly.
- // FIXME(#100717): can a `Cow<'static, str>` be used here?
- Eager(String),
+ Eager(Cow<'static, str>),
/// Identifier of a Fluent message. Instances of this variant are generated by the
/// `Subdiagnostic` derive.
FluentIdentifier(FluentId),
@@ -290,17 +287,17 @@ pub enum SubdiagnosticMessage {
impl From<String> for SubdiagnosticMessage {
fn from(s: String) -> Self {
- SubdiagnosticMessage::Str(s)
+ SubdiagnosticMessage::Str(Cow::Owned(s))
}
}
-impl<'a> From<&'a str> for SubdiagnosticMessage {
- fn from(s: &'a str) -> Self {
- SubdiagnosticMessage::Str(s.to_string())
+impl From<&'static str> for SubdiagnosticMessage {
+ fn from(s: &'static str) -> Self {
+ SubdiagnosticMessage::Str(Cow::Borrowed(s))
}
}
impl From<Cow<'static, str>> for SubdiagnosticMessage {
fn from(s: Cow<'static, str>) -> Self {
- SubdiagnosticMessage::Str(s.to_string())
+ SubdiagnosticMessage::Str(s)
}
}
@@ -312,8 +309,7 @@ impl From<Cow<'static, str>> for SubdiagnosticMessage {
#[rustc_diagnostic_item = "DiagnosticMessage"]
pub enum DiagnosticMessage {
/// Non-translatable diagnostic message.
- // FIXME(#100717): can a `Cow<'static, str>` be used here?
- Str(String),
+ Str(Cow<'static, str>),
/// Translatable message which has already been translated eagerly.
///
/// Some diagnostics have repeated subdiagnostics where the same interpolated variables would
@@ -324,8 +320,7 @@ pub enum DiagnosticMessage {
/// incorrect diagnostics. Eager translation results in translation for a subdiagnostic
/// happening immediately after the subdiagnostic derive's logic has been run. This variant
/// stores messages which have been translated eagerly.
- // FIXME(#100717): can a `Cow<'static, str>` be used here?
- Eager(String),
+ Eager(Cow<'static, str>),
/// Identifier for a Fluent message (with optional attribute) corresponding to the diagnostic
/// message.
///
@@ -363,17 +358,17 @@ impl DiagnosticMessage {
impl From<String> for DiagnosticMessage {
fn from(s: String) -> Self {
- DiagnosticMessage::Str(s)
+ DiagnosticMessage::Str(Cow::Owned(s))
}
}
-impl<'a> From<&'a str> for DiagnosticMessage {
- fn from(s: &'a str) -> Self {
- DiagnosticMessage::Str(s.to_string())
+impl From<&'static str> for DiagnosticMessage {
+ fn from(s: &'static str) -> Self {
+ DiagnosticMessage::Str(Cow::Borrowed(s))
}
}
impl From<Cow<'static, str>> for DiagnosticMessage {
fn from(s: Cow<'static, str>) -> Self {
- DiagnosticMessage::Str(s.to_string())
+ DiagnosticMessage::Str(s)
}
}
@@ -496,6 +491,10 @@ impl MultiSpan {
replacements_occurred
}
+ pub fn pop_span_label(&mut self) -> Option<(Span, DiagnosticMessage)> {
+ self.span_labels.pop()
+ }
+
/// Returns the strings to highlight. We always ensure that there
/// is an entry for each of the primary spans -- for each primary
/// span `P`, if there is at least one label with span `P`, we return
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index bd3033fcb..e8bcd7c11 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -20,7 +20,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_type_ir = { path = "../rustc_type_ir" }
unicode-width = "0.1.4"
-termcolor = "1.0"
+termcolor = "1.2.0"
annotate-snippets = "0.9"
termize = "0.1.1"
serde = { version = "1.0.125", features = [ "derive" ] }
diff --git a/compiler/rustc_errors/messages.ftl b/compiler/rustc_errors/messages.ftl
index 337097343..8e8223c3c 100644
--- a/compiler/rustc_errors/messages.ftl
+++ b/compiler/rustc_errors/messages.ftl
@@ -8,7 +8,11 @@ errors_target_invalid_address_space =
invalid address space `{$addr_space}` for `{$cause}` in "data-layout": {$err}
errors_target_invalid_alignment =
- invalid alignment for `{$cause}` in "data-layout": {$err}
+ invalid alignment for `{$cause}` in "data-layout": `{$align}` is {$err_kind ->
+ [not_power_of_two] not a power of 2
+ [too_large] too large
+ *[other] {""}
+ }
errors_target_invalid_bits =
invalid {$kind} `{$bit}` for `{$cause}` in "data-layout": {$err}
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 29c692128..ed0d06ed0 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -10,7 +10,7 @@ use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use std::borrow::Cow;
-use std::fmt;
+use std::fmt::{self, Debug};
use std::hash::{Hash, Hasher};
use std::panic::Location;
@@ -33,7 +33,7 @@ pub type DiagnosticArgName<'source> = Cow<'source, str>;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
pub enum DiagnosticArgValue<'source> {
Str(Cow<'source, str>),
- Number(usize),
+ Number(i128),
StrListSepByAnd(Vec<Cow<'source, str>>),
}
@@ -352,14 +352,9 @@ impl Diagnostic {
/// Labels all the given spans with the provided label.
/// See [`Self::span_label()`] for more information.
- pub fn span_labels(
- &mut self,
- spans: impl IntoIterator<Item = Span>,
- label: impl AsRef<str>,
- ) -> &mut Self {
- let label = label.as_ref();
+ pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
for span in spans {
- self.span_label(span, label);
+ self.span_label(span, label.to_string());
}
self
}
@@ -394,17 +389,18 @@ impl Diagnostic {
expected: DiagnosticStyledString,
found: DiagnosticStyledString,
) -> &mut Self {
- let mut msg: Vec<_> = vec![("required when trying to coerce from type `", Style::NoStyle)];
+ let mut msg: Vec<_> =
+ vec![(Cow::from("required when trying to coerce from type `"), Style::NoStyle)];
msg.extend(expected.0.iter().map(|x| match *x {
- StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle),
- StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight),
+ StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
+ StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
}));
- msg.push(("` to type '", Style::NoStyle));
+ msg.push((Cow::from("` to type '"), Style::NoStyle));
msg.extend(found.0.iter().map(|x| match *x {
- StringPart::Normal(ref s) => (s.as_str(), Style::NoStyle),
- StringPart::Highlighted(ref s) => (s.as_str(), Style::Highlight),
+ StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
+ StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
}));
- msg.push(("`", Style::NoStyle));
+ msg.push((Cow::from("`"), Style::NoStyle));
// For now, just attach these as notes
self.highlighted_note(msg);
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index db97d96fc..08ff2cfba 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -115,36 +115,22 @@ pub trait EmissionGuarantee: Sized {
) -> DiagnosticBuilder<'_, Self>;
}
-/// Private module for sealing the `IsError` helper trait.
-mod sealed_level_is_error {
- use crate::Level;
-
- /// Sealed helper trait for statically checking that a `Level` is an error.
- pub(crate) trait IsError<const L: Level> {}
-
- impl IsError<{ Level::Bug }> for () {}
- impl IsError<{ Level::DelayedBug }> for () {}
- impl IsError<{ Level::Fatal }> for () {}
- // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints
- // don't need error guarantees, as their levels are always dynamic.
- impl IsError<{ Level::Error { lint: false } }> for () {}
-}
-
impl<'a> DiagnosticBuilder<'a, ErrorGuaranteed> {
/// Convenience function for internal use, clients should use one of the
/// `struct_*` methods on [`Handler`].
#[track_caller]
- pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>, const L: Level>(
+ pub(crate) fn new_guaranteeing_error<M: Into<DiagnosticMessage>>(
handler: &'a Handler,
message: M,
- ) -> Self
- where
- (): sealed_level_is_error::IsError<L>,
- {
+ ) -> Self {
Self {
inner: DiagnosticBuilderInner {
state: DiagnosticBuilderState::Emittable(handler),
- diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)),
+ diagnostic: Box::new(Diagnostic::new_with_code(
+ Level::Error { lint: false },
+ None,
+ message,
+ )),
},
_marker: PhantomData,
}
@@ -203,9 +189,7 @@ impl EmissionGuarantee for ErrorGuaranteed {
handler: &Handler,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, Self> {
- DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(
- handler, msg,
- )
+ DiagnosticBuilder::new_guaranteeing_error(handler, msg)
}
}
@@ -558,7 +542,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
}
// Take the `Diagnostic` by replacing it with a dummy.
- let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::Str("".to_string()));
+ let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from(""));
let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
// Disable the ICE on `Drop`.
@@ -627,7 +611,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
pub fn span_labels(
&mut self,
spans: impl IntoIterator<Item = Span>,
- label: impl AsRef<str>,
+ label: &str,
) -> &mut Self);
forward!(pub fn note_expected_found(
@@ -781,8 +765,8 @@ impl Drop for DiagnosticBuilderInner<'_> {
if !panicking() {
handler.emit_diagnostic(&mut Diagnostic::new(
Level::Bug,
- DiagnosticMessage::Str(
- "the following error was constructed but not emitted".to_string(),
+ DiagnosticMessage::from(
+ "the following error was constructed but not emitted",
),
));
handler.emit_diagnostic(&mut self.diagnostic);
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 65f8a61a3..10fe7fc74 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -60,10 +60,8 @@ into_diagnostic_arg_using_display!(
u8,
i16,
u16,
- i32,
u32,
i64,
- u64,
i128,
u128,
std::io::Error,
@@ -80,6 +78,18 @@ into_diagnostic_arg_using_display!(
ExitStatus,
);
+impl IntoDiagnosticArg for i32 {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Number(self.into())
+ }
+}
+
+impl IntoDiagnosticArg for u64 {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Number(self.into())
+ }
+}
+
impl IntoDiagnosticArg for bool {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
if self {
@@ -134,7 +144,7 @@ impl IntoDiagnosticArg for PathBuf {
impl IntoDiagnosticArg for usize {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
- DiagnosticArgValue::Number(self)
+ DiagnosticArgValue::Number(self as i128)
}
}
@@ -147,9 +157,9 @@ impl IntoDiagnosticArg for PanicStrategy {
impl IntoDiagnosticArg for hir::ConstContext {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
DiagnosticArgValue::Str(Cow::Borrowed(match self {
- hir::ConstContext::ConstFn => "constant function",
+ hir::ConstContext::ConstFn => "const_fn",
hir::ConstContext::Static(_) => "static",
- hir::ConstContext::Const => "constant",
+ hir::ConstContext::Const => "const",
}))
}
}
@@ -254,7 +264,8 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
diag = handler.struct_fatal(fluent::errors_target_invalid_alignment);
diag.set_arg("cause", cause);
- diag.set_arg("err", err);
+ diag.set_arg("err_kind", err.diag_ident());
+ diag.set_arg("align", err.align());
diag
}
TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index e8cd7eaa6..9d4d159fd 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -367,7 +367,7 @@ pub trait Emitter: Translate {
children.push(SubDiagnostic {
level: Level::Note,
- message: vec![(DiagnosticMessage::Str(msg), Style::NoStyle)],
+ message: vec![(DiagnosticMessage::from(msg), Style::NoStyle)],
span: MultiSpan::new(),
render_span: None,
});
@@ -616,7 +616,7 @@ pub enum ColorConfig {
}
impl ColorConfig {
- fn to_color_choice(self) -> ColorChoice {
+ pub fn to_color_choice(self) -> ColorChoice {
match self {
ColorConfig::Always => {
if io::stderr().is_terminal() {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 3dec0d929..b9db25103 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -4,9 +4,8 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
-#![feature(drain_filter)]
+#![feature(extract_if)]
#![feature(if_let_guard)]
-#![feature(adt_const_params)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(result_option_inspect)]
@@ -62,6 +61,7 @@ pub mod emitter;
pub mod error;
pub mod json;
mod lock;
+pub mod markdown;
pub mod registry;
mod snippet;
mod styled_buffer;
@@ -384,7 +384,7 @@ pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
pub use diagnostic_impls::{
DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans,
};
-use std::backtrace::Backtrace;
+use std::backtrace::{Backtrace, BacktraceStatus};
/// A handler deals with errors and other compiler output.
/// Certain errors (fatal, bug, unimpl) may cause immediate exit,
@@ -628,7 +628,7 @@ impl Handler {
message: DiagnosticMessage,
args: impl Iterator<Item = DiagnosticArg<'a, 'static>>,
) -> SubdiagnosticMessage {
- SubdiagnosticMessage::Eager(self.eagerly_translate_to_string(message, args))
+ SubdiagnosticMessage::Eager(Cow::from(self.eagerly_translate_to_string(message, args)))
}
/// Translate `message` eagerly with `args` to `String`.
@@ -845,7 +845,7 @@ impl Handler {
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
- DiagnosticBuilder::new_guaranteeing_error::<_, { Level::Error { lint: false } }>(self, msg)
+ DiagnosticBuilder::new_guaranteeing_error(self, msg)
}
/// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
@@ -1332,7 +1332,7 @@ impl HandlerInner {
// once *any* errors were emitted (and truncate `delayed_span_bugs`
// when an error is first emitted, also), but maybe there's a case
// in which that's not sound? otherwise this is really inefficient.
- let backtrace = std::backtrace::Backtrace::force_capture();
+ let backtrace = std::backtrace::Backtrace::capture();
self.delayed_span_bugs
.push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
@@ -1400,7 +1400,7 @@ impl HandlerInner {
!self.emitted_diagnostics.insert(diagnostic_hash)
};
- diagnostic.children.drain_filter(already_emitted_sub).for_each(|_| {});
+ diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
self.emitter.emit_diagnostic(diagnostic);
if diagnostic.is_error() {
@@ -1450,14 +1450,14 @@ impl HandlerInner {
self.emit_stashed_diagnostics();
let warnings = match self.deduplicated_warn_count {
- 0 => String::new(),
- 1 => "1 warning emitted".to_string(),
- count => format!("{count} warnings emitted"),
+ 0 => Cow::from(""),
+ 1 => Cow::from("1 warning emitted"),
+ count => Cow::from(format!("{count} warnings emitted")),
};
let errors = match self.deduplicated_err_count {
- 0 => String::new(),
- 1 => "aborting due to previous error".to_string(),
- count => format!("aborting due to {count} previous errors"),
+ 0 => Cow::from(""),
+ 1 => Cow::from("aborting due to previous error"),
+ count => Cow::from(format!("aborting due to {count} previous errors")),
};
if self.treat_err_as_bug() {
return;
@@ -1621,7 +1621,7 @@ impl HandlerInner {
if self.flags.report_delayed_bugs {
self.emit_diagnostic(&mut diagnostic);
}
- let backtrace = std::backtrace::Backtrace::force_capture();
+ let backtrace = std::backtrace::Backtrace::capture();
self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
}
@@ -1740,7 +1740,17 @@ impl DelayedDiagnostic {
}
fn decorate(mut self) -> Diagnostic {
- self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note));
+ match self.note.status() {
+ BacktraceStatus::Captured => {
+ self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note));
+ }
+ // Avoid the needless newline when no backtrace has been captured,
+ // the display impl should just be a single line.
+ _ => {
+ self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note));
+ }
+ }
+
self.inner
}
}
diff --git a/compiler/rustc_errors/src/markdown/mod.rs b/compiler/rustc_errors/src/markdown/mod.rs
new file mode 100644
index 000000000..53b766dfc
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/mod.rs
@@ -0,0 +1,76 @@
+//! A simple markdown parser that can write formatted text to the terminal
+//!
+//! Entrypoint is `MdStream::parse_str(...)`
+use std::io;
+
+use termcolor::{Buffer, BufferWriter, ColorChoice};
+mod parse;
+mod term;
+
+/// An AST representation of a Markdown document
+#[derive(Clone, Debug, Default, PartialEq)]
+pub struct MdStream<'a>(Vec<MdTree<'a>>);
+
+impl<'a> MdStream<'a> {
+ /// Parse a markdown string to a tokenstream
+ #[must_use]
+ pub fn parse_str(s: &str) -> MdStream<'_> {
+ parse::entrypoint(s)
+ }
+
+ /// Write formatted output to a termcolor buffer
+ pub fn write_termcolor_buf(&self, buf: &mut Buffer) -> io::Result<()> {
+ term::entrypoint(self, buf)
+ }
+}
+
+/// Create a termcolor buffer with the `Always` color choice
+pub fn create_stdout_bufwtr() -> BufferWriter {
+ BufferWriter::stdout(ColorChoice::Always)
+}
+
+/// A single tokentree within a Markdown document
+#[derive(Clone, Debug, PartialEq)]
+pub enum MdTree<'a> {
+ /// Leaf types
+ Comment(&'a str),
+ CodeBlock {
+ txt: &'a str,
+ lang: Option<&'a str>,
+ },
+ CodeInline(&'a str),
+ Strong(&'a str),
+ Emphasis(&'a str),
+ Strikethrough(&'a str),
+ PlainText(&'a str),
+ /// [Foo](www.foo.com) or simple anchor <www.foo.com>
+ Link {
+ disp: &'a str,
+ link: &'a str,
+ },
+ /// `[Foo link][ref]`
+ RefLink {
+ disp: &'a str,
+ id: Option<&'a str>,
+ },
+ /// [ref]: www.foo.com
+ LinkDef {
+ id: &'a str,
+ link: &'a str,
+ },
+ /// Break bewtween two paragraphs (double `\n`), not directly parsed but
+ /// added later
+ ParagraphBreak,
+ /// Break bewtween two lines (single `\n`)
+ LineBreak,
+ HorizontalRule,
+ Heading(u8, MdStream<'a>),
+ OrderedListItem(u16, MdStream<'a>),
+ UnorderedListItem(MdStream<'a>),
+}
+
+impl<'a> From<Vec<MdTree<'a>>> for MdStream<'a> {
+ fn from(value: Vec<MdTree<'a>>) -> Self {
+ Self(value)
+ }
+}
diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs
new file mode 100644
index 000000000..362a451fd
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/parse.rs
@@ -0,0 +1,588 @@
+use crate::markdown::{MdStream, MdTree};
+use std::{iter, mem, str};
+
+/// Short aliases that we can use in match patterns. If an end pattern is not
+/// included, this type may be variable
+const ANC_E: &[u8] = b">";
+const ANC_S: &[u8] = b"<";
+const BRK: &[u8] = b"---";
+const CBK: &[u8] = b"```";
+const CIL: &[u8] = b"`";
+const CMT_E: &[u8] = b"-->";
+const CMT_S: &[u8] = b"<!--";
+const EMP: &[u8] = b"_";
+const HDG: &[u8] = b"#";
+const LNK_CHARS: &str = "$-_.+!*'()/&?=:%";
+const LNK_E: &[u8] = b"]";
+const LNK_S: &[u8] = b"[";
+const STG: &[u8] = b"**";
+const STK: &[u8] = b"~~";
+const UL1: &[u8] = b"* ";
+const UL2: &[u8] = b"- ";
+
+/// Pattern replacements
+const REPLACEMENTS: &[(&str, &str)] = &[
+ ("(c)", "©"),
+ ("(C)", "©"),
+ ("(r)", "®"),
+ ("(R)", "®"),
+ ("(tm)", "â„¢"),
+ ("(TM)", "â„¢"),
+ (":crab:", "🦀"),
+ ("\n", " "),
+];
+
+/// `(extracted, remaining)`
+type Parsed<'a> = (MdTree<'a>, &'a [u8]);
+/// Output of a parse function
+type ParseResult<'a> = Option<Parsed<'a>>;
+
+/// Parsing context
+#[derive(Clone, Copy, Debug, PartialEq)]
+struct Context {
+ /// If true, we are at a the topmost level (not recursing a nested tt)
+ top_block: bool,
+ /// Previous character
+ prev: Prev,
+}
+
+/// Character class preceding this one
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum Prev {
+ Newline,
+ /// Whitespace that is not a newline
+ Whitespace,
+ Escape,
+ Any,
+}
+
+impl Default for Context {
+ /// Most common setting for non top-level parsing: not top block, not at
+ /// line start (yes leading whitespace, not escaped)
+ fn default() -> Self {
+ Self { top_block: false, prev: Prev::Whitespace }
+ }
+}
+
+/// Flags to simple parser function
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum ParseOpt {
+ /// Ignore escapes before closing pattern, trim content
+ TrimNoEsc,
+ None,
+}
+
+/// Parse a buffer
+pub fn entrypoint(txt: &str) -> MdStream<'_> {
+ let ctx = Context { top_block: true, prev: Prev::Newline };
+ normalize(parse_recursive(txt.trim().as_bytes(), ctx), &mut Vec::new())
+}
+
+/// Parse a buffer with specified context
+fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> {
+ use ParseOpt as Po;
+ use Prev::{Escape, Newline, Whitespace};
+
+ let mut stream: Vec<MdTree<'a>> = Vec::new();
+ let Context { top_block: top_blk, mut prev } = ctx;
+
+ // wip_buf is our entire unprocessed (unpushed) buffer, loop_buf is our to
+ // check buffer that shrinks with each loop
+ let mut wip_buf = buf;
+ let mut loop_buf = wip_buf;
+
+ while !loop_buf.is_empty() {
+ let next_prev = match loop_buf[0] {
+ b'\n' => Newline,
+ b'\\' => Escape,
+ x if x.is_ascii_whitespace() => Whitespace,
+ _ => Prev::Any,
+ };
+
+ let res: ParseResult<'_> = match (top_blk, prev) {
+ (_, Newline | Whitespace) if loop_buf.starts_with(CMT_S) => {
+ parse_simple_pat(loop_buf, CMT_S, CMT_E, Po::TrimNoEsc, MdTree::Comment)
+ }
+ (true, Newline) if loop_buf.starts_with(CBK) => Some(parse_codeblock(loop_buf)),
+ (_, Newline | Whitespace) if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf),
+ (true, Newline | Whitespace) if loop_buf.starts_with(HDG) => parse_heading(loop_buf),
+ (true, Newline) if loop_buf.starts_with(BRK) => {
+ Some((MdTree::HorizontalRule, parse_to_newline(loop_buf).1))
+ }
+ (_, Newline | Whitespace) if loop_buf.starts_with(EMP) => {
+ parse_simple_pat(loop_buf, EMP, EMP, Po::None, MdTree::Emphasis)
+ }
+ (_, Newline | Whitespace) if loop_buf.starts_with(STG) => {
+ parse_simple_pat(loop_buf, STG, STG, Po::None, MdTree::Strong)
+ }
+ (_, Newline | Whitespace) if loop_buf.starts_with(STK) => {
+ parse_simple_pat(loop_buf, STK, STK, Po::None, MdTree::Strikethrough)
+ }
+ (_, Newline | Whitespace) if loop_buf.starts_with(ANC_S) => {
+ let tt_fn = |link| MdTree::Link { disp: link, link };
+ let ret = parse_simple_pat(loop_buf, ANC_S, ANC_E, Po::None, tt_fn);
+ match ret {
+ Some((MdTree::Link { disp, .. }, _))
+ if disp.chars().all(|ch| LNK_CHARS.contains(ch)) =>
+ {
+ ret
+ }
+ _ => None,
+ }
+ }
+ (_, Newline) if (loop_buf.starts_with(UL1) || loop_buf.starts_with(UL2)) => {
+ Some(parse_unordered_li(loop_buf))
+ }
+ (_, Newline) if ord_list_start(loop_buf).is_some() => Some(parse_ordered_li(loop_buf)),
+ (_, Newline | Whitespace) if loop_buf.starts_with(LNK_S) => {
+ parse_any_link(loop_buf, top_blk && prev == Prev::Newline)
+ }
+ (_, Escape | _) => None,
+ };
+
+ if let Some((tree, rest)) = res {
+ // We found something: push our WIP and then push the found tree
+ let prev_buf = &wip_buf[..(wip_buf.len() - loop_buf.len())];
+ if !prev_buf.is_empty() {
+ let prev_str = str::from_utf8(prev_buf).unwrap();
+ stream.push(MdTree::PlainText(prev_str));
+ }
+ stream.push(tree);
+
+ wip_buf = rest;
+ loop_buf = rest;
+ } else {
+ // Just move on to the next character
+ loop_buf = &loop_buf[1..];
+ // If we are at the end and haven't found anything, just push plain text
+ if loop_buf.is_empty() && !wip_buf.is_empty() {
+ let final_str = str::from_utf8(wip_buf).unwrap();
+ stream.push(MdTree::PlainText(final_str));
+ }
+ };
+
+ prev = next_prev;
+ }
+
+ MdStream(stream)
+}
+
+/// The simplest kind of patterns: data within start and end patterns
+fn parse_simple_pat<'a, F>(
+ buf: &'a [u8],
+ start_pat: &[u8],
+ end_pat: &[u8],
+ opts: ParseOpt,
+ create_tt: F,
+) -> ParseResult<'a>
+where
+ F: FnOnce(&'a str) -> MdTree<'a>,
+{
+ let ignore_esc = matches!(opts, ParseOpt::TrimNoEsc);
+ let trim = matches!(opts, ParseOpt::TrimNoEsc);
+ let (txt, rest) = parse_with_end_pat(&buf[start_pat.len()..], end_pat, ignore_esc)?;
+ let mut txt = str::from_utf8(txt).unwrap();
+ if trim {
+ txt = txt.trim();
+ }
+ Some((create_tt(txt), rest))
+}
+
+/// Parse backtick-wrapped inline code. Accounts for >1 backtick sets
+fn parse_codeinline(buf: &[u8]) -> ParseResult<'_> {
+ let seps = buf.iter().take_while(|ch| **ch == b'`').count();
+ let (txt, rest) = parse_with_end_pat(&buf[seps..], &buf[..seps], true)?;
+ Some((MdTree::CodeInline(str::from_utf8(txt).unwrap()), rest))
+}
+
+/// Parse a codeblock. Accounts for >3 backticks and language specification
+fn parse_codeblock(buf: &[u8]) -> Parsed<'_> {
+ // account for ````code```` style
+ let seps = buf.iter().take_while(|ch| **ch == b'`').count();
+ let end_sep = &buf[..seps];
+ let mut working = &buf[seps..];
+
+ // Handle "````rust" style language specifications
+ let next_ws_idx = working.iter().take_while(|ch| !ch.is_ascii_whitespace()).count();
+
+ let lang = if next_ws_idx > 0 {
+ // Munch the lang
+ let tmp = str::from_utf8(&working[..next_ws_idx]).unwrap();
+ working = &working[next_ws_idx..];
+ Some(tmp)
+ } else {
+ None
+ };
+
+ let mut end_pat = vec![b'\n'];
+ end_pat.extend(end_sep);
+
+ // Find first end pattern with nothing else on its line
+ let mut found = None;
+ for idx in (0..working.len()).filter(|idx| working[*idx..].starts_with(&end_pat)) {
+ let (eol_txt, rest) = parse_to_newline(&working[(idx + end_pat.len())..]);
+ if !eol_txt.iter().any(u8::is_ascii_whitespace) {
+ found = Some((&working[..idx], rest));
+ break;
+ }
+ }
+
+ let (txt, rest) = found.unwrap_or((working, &[]));
+ let txt = str::from_utf8(txt).unwrap().trim_matches('\n');
+
+ (MdTree::CodeBlock { txt, lang }, rest)
+}
+
+fn parse_heading(buf: &[u8]) -> ParseResult<'_> {
+ let level = buf.iter().take_while(|ch| **ch == b'#').count();
+ let buf = &buf[level..];
+
+ if level > 6 || (buf.len() > 1 && !buf[0].is_ascii_whitespace()) {
+ // Enforce max 6 levels and whitespace following the `##` pattern
+ return None;
+ }
+
+ let (txt, rest) = parse_to_newline(&buf[1..]);
+ let ctx = Context { top_block: false, prev: Prev::Whitespace };
+ let stream = parse_recursive(txt, ctx);
+
+ Some((MdTree::Heading(level.try_into().unwrap(), stream), rest))
+}
+
+/// Bulleted list
+fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> {
+ debug_assert!(buf.starts_with(b"* ") || buf.starts_with(b"- "));
+ let (txt, rest) = get_indented_section(&buf[2..]);
+ let ctx = Context { top_block: false, prev: Prev::Whitespace };
+ let stream = parse_recursive(trim_ascii_start(txt), ctx);
+ (MdTree::UnorderedListItem(stream), rest)
+}
+
+/// Numbered list
+fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> {
+ let (num, pos) = ord_list_start(buf).unwrap(); // success tested in caller
+ let (txt, rest) = get_indented_section(&buf[pos..]);
+ let ctx = Context { top_block: false, prev: Prev::Whitespace };
+ let stream = parse_recursive(trim_ascii_start(txt), ctx);
+ (MdTree::OrderedListItem(num, stream), rest)
+}
+
+/// Find first line that isn't empty or doesn't start with whitespace, that will
+/// be our contents
+fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) {
+ let mut end = buf.len();
+ for (idx, window) in buf.windows(2).enumerate() {
+ let &[ch, next_ch] = window else {unreachable!("always 2 elements")};
+ if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' {
+ // End of stream
+ end = buf.len().saturating_sub(1);
+ break;
+ } else if ch == b'\n' && (!next_ch.is_ascii_whitespace() || next_ch == b'\n') {
+ end = idx;
+ break;
+ }
+ }
+
+ (&buf[..end], &buf[end..])
+}
+
+/// Verify a valid ordered list start (e.g. `1.`) and parse it. Returns the
+/// parsed number and offset of character after the dot.
+fn ord_list_start(buf: &[u8]) -> Option<(u16, usize)> {
+ let pos = buf.iter().take(10).position(|ch| *ch == b'.')?;
+ let n = str::from_utf8(&buf[..pos]).ok()?;
+ if !buf.get(pos + 1)?.is_ascii_whitespace() {
+ return None;
+ }
+ n.parse::<u16>().ok().map(|v| (v, pos + 2))
+}
+
+/// Parse links. `can_be_def` indicates that a link definition is possible (top
+/// level, located at the start of a line)
+fn parse_any_link(buf: &[u8], can_be_def: bool) -> ParseResult<'_> {
+ let (bracketed, rest) = parse_with_end_pat(&buf[1..], LNK_E, true)?;
+ if rest.is_empty() {
+ return None;
+ }
+
+ let disp = str::from_utf8(bracketed).unwrap();
+ match (can_be_def, rest[0]) {
+ (true, b':') => {
+ let (link, tmp) = parse_to_newline(&rest[1..]);
+ let link = str::from_utf8(link).unwrap().trim();
+ Some((MdTree::LinkDef { id: disp, link }, tmp))
+ }
+ (_, b'(') => parse_simple_pat(rest, b"(", b")", ParseOpt::TrimNoEsc, |link| MdTree::Link {
+ disp,
+ link,
+ }),
+ (_, b'[') => parse_simple_pat(rest, b"[", b"]", ParseOpt::TrimNoEsc, |id| {
+ MdTree::RefLink { disp, id: Some(id) }
+ }),
+ _ => Some((MdTree::RefLink { disp, id: None }, rest)),
+ }
+}
+
+/// Find and consume an end pattern, return `(match, residual)`
+fn parse_with_end_pat<'a>(
+ buf: &'a [u8],
+ end_sep: &[u8],
+ ignore_esc: bool,
+) -> Option<(&'a [u8], &'a [u8])> {
+ // Find positions that start with the end seperator
+ for idx in (0..buf.len()).filter(|idx| buf[*idx..].starts_with(end_sep)) {
+ if !ignore_esc && idx > 0 && buf[idx - 1] == b'\\' {
+ continue;
+ }
+ return Some((&buf[..idx], &buf[idx + end_sep.len()..]));
+ }
+ None
+}
+
+/// Resturn `(match, residual)` to end of line. The EOL is returned with the
+/// residual.
+fn parse_to_newline(buf: &[u8]) -> (&[u8], &[u8]) {
+ buf.iter().position(|ch| *ch == b'\n').map_or((buf, &[]), |pos| buf.split_at(pos))
+}
+
+/// Take a parsed stream and fix the little things
+fn normalize<'a>(MdStream(stream): MdStream<'a>, linkdefs: &mut Vec<MdTree<'a>>) -> MdStream<'a> {
+ let mut new_stream = Vec::with_capacity(stream.len());
+ let new_defs = stream.iter().filter(|tt| matches!(tt, MdTree::LinkDef { .. }));
+ linkdefs.extend(new_defs.cloned());
+
+ // Run plaintest expansions on types that need it, call this function on nested types
+ for item in stream {
+ match item {
+ MdTree::PlainText(txt) => expand_plaintext(txt, &mut new_stream, MdTree::PlainText),
+ MdTree::Strong(txt) => expand_plaintext(txt, &mut new_stream, MdTree::Strong),
+ MdTree::Emphasis(txt) => expand_plaintext(txt, &mut new_stream, MdTree::Emphasis),
+ MdTree::Strikethrough(txt) => {
+ expand_plaintext(txt, &mut new_stream, MdTree::Strikethrough);
+ }
+ MdTree::RefLink { disp, id } => new_stream.push(match_reflink(linkdefs, disp, id)),
+ MdTree::OrderedListItem(n, st) => {
+ new_stream.push(MdTree::OrderedListItem(n, normalize(st, linkdefs)));
+ }
+ MdTree::UnorderedListItem(st) => {
+ new_stream.push(MdTree::UnorderedListItem(normalize(st, linkdefs)));
+ }
+ MdTree::Heading(n, st) => new_stream.push(MdTree::Heading(n, normalize(st, linkdefs))),
+ _ => new_stream.push(item),
+ }
+ }
+
+ // Remove non printing types, duplicate paragraph breaks, and breaks at start/end
+ new_stream.retain(|x| !matches!(x, MdTree::Comment(_) | MdTree::LinkDef { .. }));
+ new_stream.dedup_by(|r, l| matches!((r, l), (MdTree::ParagraphBreak, MdTree::ParagraphBreak)));
+
+ if new_stream.first().is_some_and(is_break_ty) {
+ new_stream.remove(0);
+ }
+ if new_stream.last().is_some_and(is_break_ty) {
+ new_stream.pop();
+ }
+
+ // Remove paragraph breaks that shouldn't be there. w[1] is what will be
+ // removed in these cases. Note that these are the items to keep, not delete
+ // (for `retain`)
+ let to_keep: Vec<bool> = new_stream
+ .windows(3)
+ .map(|w| {
+ !((matches!(&w[1], MdTree::ParagraphBreak)
+ && matches!(should_break(&w[0], &w[2]), BreakRule::Always(1) | BreakRule::Never))
+ || (matches!(&w[1], MdTree::PlainText(txt) if txt.trim().is_empty())
+ && matches!(
+ should_break(&w[0], &w[2]),
+ BreakRule::Always(_) | BreakRule::Never
+ )))
+ })
+ .collect();
+ let mut iter = iter::once(true).chain(to_keep).chain(iter::once(true));
+ new_stream.retain(|_| iter.next().unwrap());
+
+ // Insert line or paragraph breaks where there should be some
+ let mut insertions = 0;
+ let to_insert: Vec<(usize, MdTree<'_>)> = new_stream
+ .windows(2)
+ .enumerate()
+ .filter_map(|(idx, w)| match should_break(&w[0], &w[1]) {
+ BreakRule::Always(1) => Some((idx, MdTree::LineBreak)),
+ BreakRule::Always(2) => Some((idx, MdTree::ParagraphBreak)),
+ _ => None,
+ })
+ .map(|(idx, tt)| {
+ insertions += 1;
+ (idx + insertions, tt)
+ })
+ .collect();
+ to_insert.into_iter().for_each(|(idx, tt)| new_stream.insert(idx, tt));
+
+ MdStream(new_stream)
+}
+
+/// Whether two types should or shouldn't have a paragraph break between them
+#[derive(Clone, Copy, Debug, PartialEq)]
+enum BreakRule {
+ Always(u8),
+ Never,
+ Optional,
+}
+
+/// Blocks that automatically handle their own text wrapping
+fn should_break(left: &MdTree<'_>, right: &MdTree<'_>) -> BreakRule {
+ use MdTree::*;
+
+ match (left, right) {
+ // Separate these types with a single line
+ (HorizontalRule, _)
+ | (_, HorizontalRule)
+ | (OrderedListItem(_, _), OrderedListItem(_, _))
+ | (UnorderedListItem(_), UnorderedListItem(_)) => BreakRule::Always(1),
+ // Condensed types shouldn't have an extra break on either side
+ (Comment(_) | ParagraphBreak | Heading(_, _), _) | (_, Comment(_) | ParagraphBreak) => {
+ BreakRule::Never
+ }
+ // Block types should always be separated by full breaks
+ (CodeBlock { .. } | OrderedListItem(_, _) | UnorderedListItem(_), _)
+ | (_, CodeBlock { .. } | Heading(_, _) | OrderedListItem(_, _) | UnorderedListItem(_)) => {
+ BreakRule::Always(2)
+ }
+ // Text types may or may not be separated by a break
+ (
+ CodeInline(_)
+ | Strong(_)
+ | Emphasis(_)
+ | Strikethrough(_)
+ | PlainText(_)
+ | Link { .. }
+ | RefLink { .. }
+ | LinkDef { .. },
+ CodeInline(_)
+ | Strong(_)
+ | Emphasis(_)
+ | Strikethrough(_)
+ | PlainText(_)
+ | Link { .. }
+ | RefLink { .. }
+ | LinkDef { .. },
+ ) => BreakRule::Optional,
+ (LineBreak, _) | (_, LineBreak) => {
+ unreachable!("should have been removed during deduplication")
+ }
+ }
+}
+
+/// Types that indicate some form of break
+fn is_break_ty(val: &MdTree<'_>) -> bool {
+ matches!(val, MdTree::ParagraphBreak | MdTree::LineBreak)
+ // >1 break between paragraphs acts as a break
+ || matches!(val, MdTree::PlainText(txt) if txt.trim().is_empty())
+}
+
+/// Perform tranformations to text. This splits paragraphs, replaces patterns,
+/// and corrects newlines.
+///
+/// To avoid allocating strings (and using a different heavier tt type), our
+/// replace method means split into three and append each. For this reason, any
+/// viewer should treat consecutive `PlainText` types as belonging to the same
+/// paragraph.
+fn expand_plaintext<'a>(
+ txt: &'a str,
+ stream: &mut Vec<MdTree<'a>>,
+ mut f: fn(&'a str) -> MdTree<'a>,
+) {
+ if txt.is_empty() {
+ return;
+ } else if txt == "\n" {
+ if let Some(tt) = stream.last() {
+ let tmp = MdTree::PlainText(" ");
+ if should_break(tt, &tmp) == BreakRule::Optional {
+ stream.push(tmp);
+ }
+ }
+ return;
+ }
+ let mut queue1 = Vec::new();
+ let mut queue2 = Vec::new();
+ let stream_start_len = stream.len();
+ for paragraph in txt.split("\n\n") {
+ if paragraph.is_empty() {
+ stream.push(MdTree::ParagraphBreak);
+ continue;
+ }
+ let paragraph = trim_extra_ws(paragraph);
+
+ queue1.clear();
+ queue1.push(paragraph);
+
+ for (from, to) in REPLACEMENTS {
+ queue2.clear();
+ for item in &queue1 {
+ for s in item.split(from) {
+ queue2.extend(&[s, to]);
+ }
+ if queue2.len() > 1 {
+ let _ = queue2.pop(); // remove last unnecessary intersperse
+ }
+ }
+ mem::swap(&mut queue1, &mut queue2);
+ }
+
+ // Make sure we don't double whitespace
+ queue1.retain(|s| !s.is_empty());
+ for idx in 0..queue1.len() {
+ queue1[idx] = trim_extra_ws(queue1[idx]);
+ if idx < queue1.len() - 1
+ && queue1[idx].ends_with(char::is_whitespace)
+ && queue1[idx + 1].starts_with(char::is_whitespace)
+ {
+ queue1[idx] = queue1[idx].trim_end();
+ }
+ }
+ stream.extend(queue1.iter().copied().filter(|txt| !txt.is_empty()).map(&mut f));
+ stream.push(MdTree::ParagraphBreak);
+ }
+
+ if stream.len() - stream_start_len > 1 {
+ let _ = stream.pop(); // remove last unnecessary intersperse
+ }
+}
+
+/// Turn reflinks (links with reference IDs) into normal standalone links using
+/// listed link definitions
+fn match_reflink<'a>(linkdefs: &[MdTree<'a>], disp: &'a str, match_id: Option<&str>) -> MdTree<'a> {
+ let to_match = match_id.unwrap_or(disp); // Match with the display name if there isn't an id
+ for def in linkdefs {
+ if let MdTree::LinkDef { id, link } = def {
+ if *id == to_match {
+ return MdTree::Link { disp, link };
+ }
+ }
+ }
+ MdTree::Link { disp, link: "" } // link not found
+}
+
+/// If there is more than one whitespace char at start or end, trim the extras
+fn trim_extra_ws(mut txt: &str) -> &str {
+ let start_ws =
+ txt.bytes().position(|ch| !ch.is_ascii_whitespace()).unwrap_or(txt.len()).saturating_sub(1);
+ txt = &txt[start_ws..];
+ let end_ws = txt
+ .bytes()
+ .rev()
+ .position(|ch| !ch.is_ascii_whitespace())
+ .unwrap_or(txt.len())
+ .saturating_sub(1);
+ &txt[..txt.len() - end_ws]
+}
+
+/// If there is more than one whitespace char at start, trim the extras
+fn trim_ascii_start(buf: &[u8]) -> &[u8] {
+ let count = buf.iter().take_while(|ch| ch.is_ascii_whitespace()).count();
+ &buf[count..]
+}
+
+#[cfg(test)]
+#[path = "tests/parse.rs"]
+mod tests;
diff --git a/compiler/rustc_errors/src/markdown/term.rs b/compiler/rustc_errors/src/markdown/term.rs
new file mode 100644
index 000000000..e45ba6d2c
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/term.rs
@@ -0,0 +1,189 @@
+use std::cell::Cell;
+use std::io::{self, Write};
+
+use termcolor::{Buffer, Color, ColorSpec, WriteColor};
+
+use crate::markdown::{MdStream, MdTree};
+
+const DEFAULT_COLUMN_WIDTH: usize = 140;
+
+thread_local! {
+ /// Track the position of viewable characters in our buffer
+ static CURSOR: Cell<usize> = Cell::new(0);
+ /// Width of the terminal
+ static WIDTH: Cell<usize> = Cell::new(DEFAULT_COLUMN_WIDTH);
+}
+
+/// Print to terminal output to a buffer
+pub fn entrypoint(stream: &MdStream<'_>, buf: &mut Buffer) -> io::Result<()> {
+ #[cfg(not(test))]
+ if let Some((w, _)) = termize::dimensions() {
+ WIDTH.with(|c| c.set(std::cmp::min(w, DEFAULT_COLUMN_WIDTH)));
+ }
+ write_stream(stream, buf, None, 0)?;
+ buf.write_all(b"\n")
+}
+
+/// Write the buffer, reset to the default style after each
+fn write_stream(
+ MdStream(stream): &MdStream<'_>,
+ buf: &mut Buffer,
+ default: Option<&ColorSpec>,
+ indent: usize,
+) -> io::Result<()> {
+ match default {
+ Some(c) => buf.set_color(c)?,
+ None => buf.reset()?,
+ }
+
+ for tt in stream {
+ write_tt(tt, buf, indent)?;
+ if let Some(c) = default {
+ buf.set_color(c)?;
+ }
+ }
+
+ buf.reset()?;
+ Ok(())
+}
+
+pub fn write_tt(tt: &MdTree<'_>, buf: &mut Buffer, indent: usize) -> io::Result<()> {
+ match tt {
+ MdTree::CodeBlock { txt, lang: _ } => {
+ buf.set_color(ColorSpec::new().set_dimmed(true))?;
+ buf.write_all(txt.as_bytes())?;
+ }
+ MdTree::CodeInline(txt) => {
+ buf.set_color(ColorSpec::new().set_dimmed(true))?;
+ write_wrapping(buf, txt, indent, None)?;
+ }
+ MdTree::Strong(txt) => {
+ buf.set_color(ColorSpec::new().set_bold(true))?;
+ write_wrapping(buf, txt, indent, None)?;
+ }
+ MdTree::Emphasis(txt) => {
+ buf.set_color(ColorSpec::new().set_italic(true))?;
+ write_wrapping(buf, txt, indent, None)?;
+ }
+ MdTree::Strikethrough(txt) => {
+ buf.set_color(ColorSpec::new().set_strikethrough(true))?;
+ write_wrapping(buf, txt, indent, None)?;
+ }
+ MdTree::PlainText(txt) => {
+ write_wrapping(buf, txt, indent, None)?;
+ }
+ MdTree::Link { disp, link } => {
+ write_wrapping(buf, disp, indent, Some(link))?;
+ }
+ MdTree::ParagraphBreak => {
+ buf.write_all(b"\n\n")?;
+ reset_cursor();
+ }
+ MdTree::LineBreak => {
+ buf.write_all(b"\n")?;
+ reset_cursor();
+ }
+ MdTree::HorizontalRule => {
+ (0..WIDTH.with(Cell::get)).for_each(|_| buf.write_all(b"-").unwrap());
+ reset_cursor();
+ }
+ MdTree::Heading(n, stream) => {
+ let mut cs = ColorSpec::new();
+ cs.set_fg(Some(Color::Cyan));
+ match n {
+ 1 => cs.set_intense(true).set_bold(true).set_underline(true),
+ 2 => cs.set_intense(true).set_underline(true),
+ 3 => cs.set_intense(true).set_italic(true),
+ 4.. => cs.set_underline(true).set_italic(true),
+ 0 => unreachable!(),
+ };
+ write_stream(stream, buf, Some(&cs), 0)?;
+ buf.write_all(b"\n")?;
+ }
+ MdTree::OrderedListItem(n, stream) => {
+ let base = format!("{n}. ");
+ write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
+ write_stream(stream, buf, None, indent + 4)?;
+ }
+ MdTree::UnorderedListItem(stream) => {
+ let base = "* ";
+ write_wrapping(buf, &format!("{base:<4}"), indent, None)?;
+ write_stream(stream, buf, None, indent + 4)?;
+ }
+ // Patterns popped in previous step
+ MdTree::Comment(_) | MdTree::LinkDef { .. } | MdTree::RefLink { .. } => unreachable!(),
+ }
+
+ buf.reset()?;
+
+ Ok(())
+}
+
+/// End of that block, just wrap the line
+fn reset_cursor() {
+ CURSOR.with(|cur| cur.set(0));
+}
+
+/// Change to be generic on Write for testing. If we have a link URL, we don't
+/// count the extra tokens to make it clickable.
+fn write_wrapping<B: io::Write>(
+ buf: &mut B,
+ text: &str,
+ indent: usize,
+ link_url: Option<&str>,
+) -> io::Result<()> {
+ let ind_ws = &b" "[..indent];
+ let mut to_write = text;
+ if let Some(url) = link_url {
+ // This is a nonprinting prefix so we don't increment our cursor
+ write!(buf, "\x1b]8;;{url}\x1b\\")?;
+ }
+ CURSOR.with(|cur| {
+ loop {
+ if cur.get() == 0 {
+ buf.write_all(ind_ws)?;
+ cur.set(indent);
+ }
+ let ch_count = WIDTH.with(Cell::get) - cur.get();
+ let mut iter = to_write.char_indices();
+ let Some((end_idx, _ch)) = iter.nth(ch_count) else {
+ // Write entire line
+ buf.write_all(to_write.as_bytes())?;
+ cur.set(cur.get()+to_write.chars().count());
+ break;
+ };
+
+ if let Some((break_idx, ch)) = to_write[..end_idx]
+ .char_indices()
+ .rev()
+ .find(|(_idx, ch)| ch.is_whitespace() || ['_', '-'].contains(ch))
+ {
+ // Found whitespace to break at
+ if ch.is_whitespace() {
+ writeln!(buf, "{}", &to_write[..break_idx])?;
+ to_write = to_write[break_idx..].trim_start();
+ } else {
+ // Break at a `-` or `_` separator
+ writeln!(buf, "{}", &to_write.get(..break_idx + 1).unwrap_or(to_write))?;
+ to_write = to_write.get(break_idx + 1..).unwrap_or_default().trim_start();
+ }
+ } else {
+ // No whitespace, we need to just split
+ let ws_idx =
+ iter.find(|(_, ch)| ch.is_whitespace()).map_or(to_write.len(), |(idx, _)| idx);
+ writeln!(buf, "{}", &to_write[..ws_idx])?;
+ to_write = to_write.get(ws_idx + 1..).map_or("", str::trim_start);
+ }
+ cur.set(0);
+ }
+ if link_url.is_some() {
+ buf.write_all(b"\x1b]8;;\x1b\\")?;
+ }
+
+ Ok(())
+ })
+}
+
+#[cfg(test)]
+#[path = "tests/term.rs"]
+mod tests;
diff --git a/compiler/rustc_errors/src/markdown/tests/input.md b/compiler/rustc_errors/src/markdown/tests/input.md
new file mode 100644
index 000000000..7d207fc42
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/input.md
@@ -0,0 +1,50 @@
+# H1 Heading [with a link][remote-link]
+
+H1 content: **some words in bold** and `so does inline code`
+
+## H2 Heading
+
+H2 content: _some words in italic_
+
+### H3 Heading
+
+H3 content: ~~strikethrough~~ text
+
+#### H4 Heading
+
+H4 content: A [simple link](https://docs.rs) and a [remote-link].
+
+---
+
+A section break was above. We can also do paragraph breaks:
+
+(new paragraph) and unordered lists:
+
+- Item 1 in `code`
+- Item 2 in _italics_
+
+Or ordered:
+
+1. Item 1 in **bold**
+2. Item 2 with some long lines that should wrap: Lorem ipsum dolor sit amet,
+ consectetur adipiscing elit. Aenean ac mattis nunc. Phasellus elit quam,
+ pulvinar ac risus in, dictum vehicula turpis. Vestibulum neque est, accumsan
+ in cursus sit amet, dictum a nunc. Suspendisse aliquet, lorem eu eleifend
+ accumsan, magna neque sodales nisi, a aliquet lectus leo eu sem.
+
+---
+
+## Code
+
+Both `inline code` and code blocks are supported:
+
+```rust
+/// A rust enum
+#[derive(Debug, PartialEq, Clone)]
+enum Foo {
+ /// Start of line
+ Bar
+}
+```
+
+[remote-link]: http://docs.rs
diff --git a/compiler/rustc_errors/src/markdown/tests/output.stdout b/compiler/rustc_errors/src/markdown/tests/output.stdout
new file mode 100644
index 000000000..23c60d5c3
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/output.stdout
@@ -0,0 +1,35 @@
+H1 Heading ]8;;http://docs.rs\with a link]8;;\
+H1 content: some words in bold and so does inline code
+
+H2 Heading
+H2 content: some words in italic
+
+H3 Heading
+H3 content: strikethrough text
+
+H4 Heading
+H4 content: A ]8;;https://docs.rs\simple link]8;;\ and a ]8;;http://docs.rs\remote-link]8;;\.
+--------------------------------------------------------------------------------------------------------------------------------------------
+A section break was above. We can also do paragraph breaks:
+
+(new paragraph) and unordered lists:
+
+* Item 1 in code
+* Item 2 in italics
+
+Or ordered:
+
+1. Item 1 in bold
+2. Item 2 with some long lines that should wrap: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ac mattis nunc. Phasellus
+ elit quam, pulvinar ac risus in, dictum vehicula turpis. Vestibulum neque est, accumsan in cursus sit amet, dictum a nunc. Suspendisse
+ aliquet, lorem eu eleifend accumsan, magna neque sodales nisi, a aliquet lectus leo eu sem.
+--------------------------------------------------------------------------------------------------------------------------------------------
+Code
+Both inline code and code blocks are supported:
+
+/// A rust enum
+#[derive(Debug, PartialEq, Clone)]
+enum Foo {
+ /// Start of line
+ Bar
+}
diff --git a/compiler/rustc_errors/src/markdown/tests/parse.rs b/compiler/rustc_errors/src/markdown/tests/parse.rs
new file mode 100644
index 000000000..e39e8c89b
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/parse.rs
@@ -0,0 +1,312 @@
+use super::*;
+use ParseOpt as PO;
+
+#[test]
+fn test_parse_simple() {
+ let buf = "**abcd** rest";
+ let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap();
+ assert_eq!(t, MdTree::Strong("abcd"));
+ assert_eq!(r, b" rest");
+
+ // Escaping should fail
+ let buf = r"**abcd\** rest";
+ let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong);
+ assert!(res.is_none());
+}
+
+#[test]
+fn test_parse_comment() {
+ let opt = PO::TrimNoEsc;
+ let buf = "<!-- foobar! -->rest";
+ let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap();
+ assert_eq!(t, MdTree::Comment("foobar!"));
+ assert_eq!(r, b"rest");
+
+ let buf = r"<!-- foobar! \-->rest";
+ let (t, r) = parse_simple_pat(buf.as_bytes(), CMT_S, CMT_E, opt, MdTree::Comment).unwrap();
+ assert_eq!(t, MdTree::Comment(r"foobar! \"));
+ assert_eq!(r, b"rest");
+}
+
+#[test]
+fn test_parse_heading() {
+ let buf1 = "# Top level\nrest";
+ let (t, r) = parse_heading(buf1.as_bytes()).unwrap();
+ assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Top level")].into()));
+ assert_eq!(r, b"\nrest");
+
+ let buf1 = "# Empty";
+ let (t, r) = parse_heading(buf1.as_bytes()).unwrap();
+ assert_eq!(t, MdTree::Heading(1, vec![MdTree::PlainText("Empty")].into()));
+ assert_eq!(r, b"");
+
+ // Combo
+ let buf2 = "### Top `level` _woo_\nrest";
+ let (t, r) = parse_heading(buf2.as_bytes()).unwrap();
+ assert_eq!(
+ t,
+ MdTree::Heading(
+ 3,
+ vec![
+ MdTree::PlainText("Top "),
+ MdTree::CodeInline("level"),
+ MdTree::PlainText(" "),
+ MdTree::Emphasis("woo"),
+ ]
+ .into()
+ )
+ );
+ assert_eq!(r, b"\nrest");
+}
+
+#[test]
+fn test_parse_code_inline() {
+ let buf1 = "`abcd` rest";
+ let (t, r) = parse_codeinline(buf1.as_bytes()).unwrap();
+ assert_eq!(t, MdTree::CodeInline("abcd"));
+ assert_eq!(r, b" rest");
+
+ // extra backticks, newline
+ let buf2 = "```ab\ncd``` rest";
+ let (t, r) = parse_codeinline(buf2.as_bytes()).unwrap();
+ assert_eq!(t, MdTree::CodeInline("ab\ncd"));
+ assert_eq!(r, b" rest");
+
+ // test no escaping
+ let buf3 = r"`abcd\` rest";
+ let (t, r) = parse_codeinline(buf3.as_bytes()).unwrap();
+ assert_eq!(t, MdTree::CodeInline(r"abcd\"));
+ assert_eq!(r, b" rest");
+}
+
+#[test]
+fn test_parse_code_block() {
+ let buf1 = "```rust\ncode\ncode\n```\nleftovers";
+ let (t, r) = parse_codeblock(buf1.as_bytes());
+ assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode", lang: Some("rust") });
+ assert_eq!(r, b"\nleftovers");
+
+ let buf2 = "`````\ncode\ncode````\n`````\nleftovers";
+ let (t, r) = parse_codeblock(buf2.as_bytes());
+ assert_eq!(t, MdTree::CodeBlock { txt: "code\ncode````", lang: None });
+ assert_eq!(r, b"\nleftovers");
+}
+
+#[test]
+fn test_parse_link() {
+ let simple = "[see here](docs.rs) other";
+ let (t, r) = parse_any_link(simple.as_bytes(), false).unwrap();
+ assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" });
+ assert_eq!(r, b" other");
+
+ let simple_toplevel = "[see here](docs.rs) other";
+ let (t, r) = parse_any_link(simple_toplevel.as_bytes(), true).unwrap();
+ assert_eq!(t, MdTree::Link { disp: "see here", link: "docs.rs" });
+ assert_eq!(r, b" other");
+
+ let reference = "[see here] other";
+ let (t, r) = parse_any_link(reference.as_bytes(), true).unwrap();
+ assert_eq!(t, MdTree::RefLink { disp: "see here", id: None });
+ assert_eq!(r, b" other");
+
+ let reference_full = "[see here][docs-rs] other";
+ let (t, r) = parse_any_link(reference_full.as_bytes(), false).unwrap();
+ assert_eq!(t, MdTree::RefLink { disp: "see here", id: Some("docs-rs") });
+ assert_eq!(r, b" other");
+
+ let reference_def = "[see here]: docs.rs\nother";
+ let (t, r) = parse_any_link(reference_def.as_bytes(), true).unwrap();
+ assert_eq!(t, MdTree::LinkDef { id: "see here", link: "docs.rs" });
+ assert_eq!(r, b"\nother");
+}
+
+const IND1: &str = r"test standard
+ ind
+ ind2
+not ind";
+const IND2: &str = r"test end of stream
+ 1
+ 2
+";
+const IND3: &str = r"test empty lines
+ 1
+ 2
+
+not ind";
+
+#[test]
+fn test_indented_section() {
+ let (t, r) = get_indented_section(IND1.as_bytes());
+ assert_eq!(str::from_utf8(t).unwrap(), "test standard\n ind\n ind2");
+ assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind");
+
+ let (txt, rest) = get_indented_section(IND2.as_bytes());
+ assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2");
+ assert_eq!(str::from_utf8(rest).unwrap(), "\n");
+
+ let (txt, rest) = get_indented_section(IND3.as_bytes());
+ assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2");
+ assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind");
+}
+
+const HBT: &str = r"# Heading
+
+content";
+
+#[test]
+fn test_heading_breaks() {
+ let expected = vec![
+ MdTree::Heading(1, vec![MdTree::PlainText("Heading")].into()),
+ MdTree::PlainText("content"),
+ ]
+ .into();
+ let res = entrypoint(HBT);
+ assert_eq!(res, expected);
+}
+
+const NL1: &str = r"start
+
+end";
+const NL2: &str = r"start
+
+
+end";
+const NL3: &str = r"start
+
+
+
+end";
+
+#[test]
+fn test_newline_breaks() {
+ let expected =
+ vec![MdTree::PlainText("start"), MdTree::ParagraphBreak, MdTree::PlainText("end")].into();
+ for (idx, check) in [NL1, NL2, NL3].iter().enumerate() {
+ let res = entrypoint(check);
+ assert_eq!(res, expected, "failed {idx}");
+ }
+}
+
+const WRAP: &str = "plain _italics
+italics_";
+
+#[test]
+fn test_wrap_pattern() {
+ let expected = vec![
+ MdTree::PlainText("plain "),
+ MdTree::Emphasis("italics"),
+ MdTree::Emphasis(" "),
+ MdTree::Emphasis("italics"),
+ ]
+ .into();
+ let res = entrypoint(WRAP);
+ assert_eq!(res, expected);
+}
+
+const WRAP_NOTXT: &str = r"_italics_
+**bold**";
+
+#[test]
+fn test_wrap_notxt() {
+ let expected =
+ vec![MdTree::Emphasis("italics"), MdTree::PlainText(" "), MdTree::Strong("bold")].into();
+ let res = entrypoint(WRAP_NOTXT);
+ assert_eq!(res, expected);
+}
+
+const MIXED_LIST: &str = r"start
+- _italics item_
+<!-- comment -->
+- **bold item**
+ second line [link1](foobar1)
+ third line [link2][link-foo]
+- :crab:
+ extra indent
+end
+[link-foo]: foobar2
+";
+
+#[test]
+fn test_list() {
+ let expected = vec![
+ MdTree::PlainText("start"),
+ MdTree::ParagraphBreak,
+ MdTree::UnorderedListItem(vec![MdTree::Emphasis("italics item")].into()),
+ MdTree::LineBreak,
+ MdTree::UnorderedListItem(
+ vec![
+ MdTree::Strong("bold item"),
+ MdTree::PlainText(" second line "),
+ MdTree::Link { disp: "link1", link: "foobar1" },
+ MdTree::PlainText(" third line "),
+ MdTree::Link { disp: "link2", link: "foobar2" },
+ ]
+ .into(),
+ ),
+ MdTree::LineBreak,
+ MdTree::UnorderedListItem(
+ vec![MdTree::PlainText("🦀"), MdTree::PlainText(" extra indent")].into(),
+ ),
+ MdTree::ParagraphBreak,
+ MdTree::PlainText("end"),
+ ]
+ .into();
+ let res = entrypoint(MIXED_LIST);
+ assert_eq!(res, expected);
+}
+
+const SMOOSHED: &str = r#"
+start
+### heading
+1. ordered item
+```rust
+println!("Hello, world!");
+```
+`inline`
+``end``
+"#;
+
+#[test]
+fn test_without_breaks() {
+ let expected = vec![
+ MdTree::PlainText("start"),
+ MdTree::ParagraphBreak,
+ MdTree::Heading(3, vec![MdTree::PlainText("heading")].into()),
+ MdTree::OrderedListItem(1, vec![MdTree::PlainText("ordered item")].into()),
+ MdTree::ParagraphBreak,
+ MdTree::CodeBlock { txt: r#"println!("Hello, world!");"#, lang: Some("rust") },
+ MdTree::ParagraphBreak,
+ MdTree::CodeInline("inline"),
+ MdTree::PlainText(" "),
+ MdTree::CodeInline("end"),
+ ]
+ .into();
+ let res = entrypoint(SMOOSHED);
+ assert_eq!(res, expected);
+}
+
+const CODE_STARTLINE: &str = r#"
+start
+`code`
+middle
+`more code`
+end
+"#;
+
+#[test]
+fn test_code_at_start() {
+ let expected = vec![
+ MdTree::PlainText("start"),
+ MdTree::PlainText(" "),
+ MdTree::CodeInline("code"),
+ MdTree::PlainText(" "),
+ MdTree::PlainText("middle"),
+ MdTree::PlainText(" "),
+ MdTree::CodeInline("more code"),
+ MdTree::PlainText(" "),
+ MdTree::PlainText("end"),
+ ]
+ .into();
+ let res = entrypoint(CODE_STARTLINE);
+ assert_eq!(res, expected);
+}
diff --git a/compiler/rustc_errors/src/markdown/tests/term.rs b/compiler/rustc_errors/src/markdown/tests/term.rs
new file mode 100644
index 000000000..3b31c6d62
--- /dev/null
+++ b/compiler/rustc_errors/src/markdown/tests/term.rs
@@ -0,0 +1,90 @@
+use std::io::BufWriter;
+use std::path::PathBuf;
+use termcolor::{BufferWriter, ColorChoice};
+
+use super::*;
+use crate::markdown::MdStream;
+
+const INPUT: &str = include_str!("input.md");
+const OUTPUT_PATH: &[&str] = &[env!("CARGO_MANIFEST_DIR"), "src","markdown","tests","output.stdout"];
+
+const TEST_WIDTH: usize = 80;
+
+// We try to make some words long to create corner cases
+const TXT: &str = r"Lorem ipsum dolor sit amet, consecteturadipiscingelit.
+Fusce-id-urna-sollicitudin, pharetra nisl nec, lobortis tellus. In at
+metus hendrerit, tincidunteratvel, ultrices turpis. Curabitur_risus_sapien,
+porta-sed-nunc-sed, ultricesposuerelacus. Sed porttitor quis
+dolor non venenatis. Aliquam ut. ";
+
+const WRAPPED: &str = r"Lorem ipsum dolor sit amet, consecteturadipiscingelit. Fusce-id-urna-
+sollicitudin, pharetra nisl nec, lobortis tellus. In at metus hendrerit,
+tincidunteratvel, ultrices turpis. Curabitur_risus_sapien, porta-sed-nunc-sed,
+ultricesposuerelacus. Sed porttitor quis dolor non venenatis. Aliquam ut. Lorem
+ ipsum dolor sit amet, consecteturadipiscingelit. Fusce-id-urna-
+ sollicitudin, pharetra nisl nec, lobortis tellus. In at metus hendrerit,
+ tincidunteratvel, ultrices turpis. Curabitur_risus_sapien, porta-sed-nunc-
+ sed, ultricesposuerelacus. Sed porttitor quis dolor non venenatis. Aliquam
+ ut. Sample link lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
+consecteturadipiscingelit. Fusce-id-urna-sollicitudin, pharetra nisl nec,
+lobortis tellus. In at metus hendrerit, tincidunteratvel, ultrices turpis.
+Curabitur_risus_sapien, porta-sed-nunc-sed, ultricesposuerelacus. Sed porttitor
+quis dolor non venenatis. Aliquam ut. ";
+
+#[test]
+fn test_wrapping_write() {
+ WIDTH.with(|w| w.set(TEST_WIDTH));
+ let mut buf = BufWriter::new(Vec::new());
+ let txt = TXT.replace("-\n","-").replace("_\n","_").replace('\n', " ").replace(" ", "");
+ write_wrapping(&mut buf, &txt, 0, None).unwrap();
+ write_wrapping(&mut buf, &txt, 4, None).unwrap();
+ write_wrapping(
+ &mut buf,
+ "Sample link lorem ipsum dolor sit amet. ",
+ 4,
+ Some("link-address-placeholder"),
+ )
+ .unwrap();
+ write_wrapping(&mut buf, &txt, 0, None).unwrap();
+ let out = String::from_utf8(buf.into_inner().unwrap()).unwrap();
+ let out = out
+ .replace("\x1b\\", "")
+ .replace('\x1b', "")
+ .replace("]8;;", "")
+ .replace("link-address-placeholder", "");
+
+ for line in out.lines() {
+ assert!(line.len() <= TEST_WIDTH, "line length\n'{line}'")
+ }
+
+ assert_eq!(out, WRAPPED);
+}
+
+#[test]
+fn test_output() {
+ // Capture `--bless` when run via ./x
+ let bless = std::env::var("RUSTC_BLESS").unwrap_or_default() == "1";
+ let ast = MdStream::parse_str(INPUT);
+ let bufwtr = BufferWriter::stderr(ColorChoice::Always);
+ let mut buffer = bufwtr.buffer();
+ ast.write_termcolor_buf(&mut buffer).unwrap();
+
+ let mut blessed = PathBuf::new();
+ blessed.extend(OUTPUT_PATH);
+
+ if bless {
+ std::fs::write(&blessed, buffer.into_inner()).unwrap();
+ eprintln!("blessed output at {}", blessed.display());
+ } else {
+ let output = buffer.into_inner();
+ if std::fs::read(blessed).unwrap() != output {
+ // hack: I don't know any way to write bytes to the captured stdout
+ // that cargo test uses
+ let mut out = std::io::stdout();
+ out.write_all(b"\n\nMarkdown output did not match. Expected:\n").unwrap();
+ out.write_all(&output).unwrap();
+ out.write_all(b"\n\n").unwrap();
+ panic!("markdown output mismatch");
+ }
+ }
+}
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 4671adccc..8a251ea29 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -947,6 +947,8 @@ pub trait ResolverExpand {
/// HIR proc macros items back to their harness items.
fn declare_proc_macro(&mut self, id: NodeId);
+ fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem);
+
/// Tools registered with `#![register_tool]` and used by tool attributes and lints.
fn registered_tools(&self) -> &RegisteredTools;
}
@@ -965,7 +967,7 @@ pub trait LintStoreExpand {
type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>;
-#[derive(Clone, Default)]
+#[derive(Debug, Clone, Default)]
pub struct ModuleData {
/// Path to the module starting from the crate name, like `my_crate::foo::bar`.
pub mod_path: Vec<Ident>,
@@ -1108,6 +1110,7 @@ impl<'a> ExtCtxt<'a> {
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn struct_span_err<S: Into<MultiSpan>>(
&self,
sp: S,
@@ -1116,6 +1119,7 @@ impl<'a> ExtCtxt<'a> {
self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg)
}
+ #[track_caller]
pub fn create_err(
&self,
err: impl IntoDiagnostic<'a>,
@@ -1123,6 +1127,7 @@ impl<'a> ExtCtxt<'a> {
self.sess.create_err(err)
}
+ #[track_caller]
pub fn emit_err(&self, err: impl IntoDiagnostic<'a>) -> ErrorGuaranteed {
self.sess.emit_err(err)
}
@@ -1133,10 +1138,12 @@ impl<'a> ExtCtxt<'a> {
/// Compilation will be stopped in the near future (at the end of
/// the macro expansion phase).
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
self.sess.parse_sess.span_diagnostic.span_err(sp, msg);
}
#[rustc_lint_diagnostics]
+ #[track_caller]
pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: impl Into<DiagnosticMessage>) {
self.sess.parse_sess.span_diagnostic.span_warn(sp, msg);
}
@@ -1154,7 +1161,7 @@ impl<'a> ExtCtxt<'a> {
// Fixme: does this result in errors?
self.expansions.clear();
}
- pub fn bug(&self, msg: &str) -> ! {
+ pub fn bug(&self, msg: &'static str) -> ! {
self.sess.parse_sess.span_diagnostic.bug(msg);
}
pub fn trace_macros(&self) -> bool {
@@ -1224,7 +1231,7 @@ pub fn resolve_path(
pub fn expr_to_spanned_string<'a>(
cx: &'a mut ExtCtxt<'_>,
expr: P<ast::Expr>,
- err_msg: &str,
+ err_msg: &'static str,
) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a, ErrorGuaranteed>, bool)>> {
// Perform eager expansion on the expression.
// We want to be able to handle e.g., `concat!("foo", "bar")`.
@@ -1262,7 +1269,7 @@ pub fn expr_to_spanned_string<'a>(
pub fn expr_to_string(
cx: &mut ExtCtxt<'_>,
expr: P<ast::Expr>,
- err_msg: &str,
+ err_msg: &'static str,
) -> Option<(Symbol, ast::StrStyle)> {
expr_to_spanned_string(cx, expr, err_msg)
.map_err(|err| {
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 4ff8e409d..3e43eae00 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -197,9 +197,11 @@ pub fn pre_configure_attrs(sess: &Session, attrs: &[Attribute]) -> ast::AttrVec
config_tokens: false,
lint_node_id: ast::CRATE_NODE_ID,
};
- let attrs: ast::AttrVec =
- attrs.iter().flat_map(|attr| strip_unconfigured.process_cfg_attr(attr)).collect();
- if strip_unconfigured.in_cfg(&attrs) { attrs } else { ast::AttrVec::new() }
+ attrs
+ .iter()
+ .flat_map(|attr| strip_unconfigured.process_cfg_attr(attr))
+ .take_while(|attr| !is_cfg(attr) || strip_unconfigured.cfg_true(attr).0)
+ .collect()
}
#[macro_export]
@@ -416,26 +418,34 @@ impl<'a> StripUnconfigured<'a> {
/// Determines if a node with the given attributes should be included in this configuration.
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
- attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr))
+ attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr).0)
}
- pub(crate) fn cfg_true(&self, attr: &Attribute) -> bool {
+ pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
Ok(meta_item) => meta_item,
Err(mut err) => {
err.emit();
- return true;
+ return (true, None);
}
};
- parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
- attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.lint_node_id, self.features)
- })
+ (
+ parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
+ attr::cfg_matches(
+ &meta_item,
+ &self.sess.parse_sess,
+ self.lint_node_id,
+ self.features,
+ )
+ }),
+ Some(meta_item),
+ )
}
/// If attributes are not allowed on expressions, emit an error for `attr`
#[instrument(level = "trace", skip(self))]
pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
- if !self.features.map_or(true, |features| features.stmt_expr_attributes) {
+ if self.features.is_some_and(|features| !features.stmt_expr_attributes) {
let mut err = feature_err(
&self.sess.parse_sess,
sym::stmt_expr_attributes,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index ce0093c7d..9850723a8 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1039,9 +1039,20 @@ trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
) -> Result<Self::OutputTy, Self> {
Ok(noop_flat_map(node, collector))
}
- fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, span: Span) {
+ fn expand_cfg_false(
+ &mut self,
+ collector: &mut InvocationCollector<'_, '_>,
+ _pos: usize,
+ span: Span,
+ ) {
collector.cx.emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}
+
+ /// All of the names (items) declared by this node.
+ /// This is an approximation and should only be used for diagnostics.
+ fn declared_names(&self) -> Vec<Ident> {
+ vec![]
+ }
}
impl InvocationCollectorNode for P<ast::Item> {
@@ -1148,6 +1159,27 @@ impl InvocationCollectorNode for P<ast::Item> {
collector.cx.current_expansion.module = orig_module;
res
}
+ fn declared_names(&self) -> Vec<Ident> {
+ if let ItemKind::Use(ut) = &self.kind {
+ fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
+ match &ut.kind {
+ ast::UseTreeKind::Glob => {}
+ ast::UseTreeKind::Simple(_) => idents.push(ut.ident()),
+ ast::UseTreeKind::Nested(nested) => {
+ for (ut, _) in nested {
+ collect_use_tree_leaves(&ut, idents);
+ }
+ }
+ }
+ }
+
+ let mut idents = Vec::new();
+ collect_use_tree_leaves(&ut, &mut idents);
+ return idents;
+ }
+
+ vec![self.ident]
+ }
}
struct TraitItemTag;
@@ -1382,8 +1414,15 @@ impl InvocationCollectorNode for ast::Crate {
fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) {
noop_visit_crate(self, visitor)
}
- fn expand_cfg_false(&mut self, collector: &mut InvocationCollector<'_, '_>, _span: Span) {
- self.attrs.clear();
+ fn expand_cfg_false(
+ &mut self,
+ collector: &mut InvocationCollector<'_, '_>,
+ pos: usize,
+ _span: Span,
+ ) {
+ // Attributes above `cfg(FALSE)` are left in place, because we may want to configure
+ // some global crate properties even on fully unconfigured crates.
+ self.attrs.truncate(pos);
// Standard prelude imports are left in the crate for backward compatibility.
self.items.truncate(collector.cx.num_standard_library_imports);
}
@@ -1685,8 +1724,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
node: &mut impl HasAttrs,
attr: ast::Attribute,
pos: usize,
- ) -> bool {
- let res = self.cfg().cfg_true(&attr);
+ ) -> (bool, Option<ast::MetaItem>) {
+ let (res, meta_item) = self.cfg().cfg_true(&attr);
if res {
// FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion,
// and some tools like rustdoc and clippy rely on that. Find a way to remove them
@@ -1694,7 +1733,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
self.cx.expanded_inert_attrs.mark(&attr);
node.visit_attrs(|attrs| attrs.insert(pos, attr));
}
- res
+
+ (res, meta_item)
}
fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) {
@@ -1715,9 +1755,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
return match self.take_first_attr(&mut node) {
Some((attr, pos, derives)) => match attr.name_or_empty() {
sym::cfg => {
- if self.expand_cfg_true(&mut node, attr, pos) {
+ let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos);
+ if res {
continue;
}
+
+ if let Some(meta_item) = meta_item {
+ for name in node.declared_names() {
+ self.cx.resolver.append_stripped_cfg_item(
+ self.cx.current_expansion.lint_node_id,
+ name,
+ meta_item.clone(),
+ )
+ }
+ }
Default::default()
}
sym::cfg_attr => {
@@ -1761,11 +1812,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
Some((attr, pos, derives)) => match attr.name_or_empty() {
sym::cfg => {
let span = attr.span;
- if self.expand_cfg_true(node, attr, pos) {
+ if self.expand_cfg_true(node, attr, pos).0 {
continue;
}
- node.expand_cfg_false(self, span);
+ node.expand_cfg_false(self, pos, span);
continue;
}
sym::cfg_attr => {
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index cb8b4899e..3593bed2d 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -170,7 +170,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
}
Error(err_sp, msg) => {
let span = err_sp.substitute_dummy(self.root_span);
- self.cx.struct_span_err(span, msg.as_str()).emit();
+ self.cx.struct_span_err(span, msg.clone()).emit();
self.result = Some(DummyResult::any(span));
}
ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
@@ -222,7 +222,7 @@ pub(super) fn emit_frag_parse_err(
{
let msg = &e.message[0];
e.message[0] = (
- DiagnosticMessage::Str(format!(
+ DiagnosticMessage::from(format!(
"macro expansion ends with an incomplete expression: {}",
message.replace(", found `<eof>`", ""),
)),
@@ -313,9 +313,9 @@ pub(super) fn annotate_doc_comment(err: &mut Diagnostic, sm: &SourceMap, span: S
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
/// other tokens, this is "unexpected token...".
-pub(super) fn parse_failure_msg(tok: &Token) -> String {
+pub(super) fn parse_failure_msg(tok: &Token) -> Cow<'static, str> {
match tok.kind {
- token::Eof => "unexpected end of macro invocation".to_string(),
- _ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),),
+ token::Eof => Cow::from("unexpected end of macro invocation"),
+ _ => Cow::from(format!("no rules expected the token `{}`", pprust::token_to_string(tok))),
}
}
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 1c222fb4a..f0e67cfd5 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
}
/// A single matcher position, representing the state of matching.
+#[derive(Debug)]
struct MatcherPos {
/// The index into `TtParser::locs`, which represents the "dot".
idx: usize,
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index e4c65a204..42cc0a6b1 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -223,8 +223,7 @@ fn expand_macro<'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 tts.len() == rhs.tts.len() {
- tts = tts.map_enumerated(|i, tt| {
- let mut tt = tt.clone();
+ tts = tts.map_enumerated_owned(|i, mut tt| {
let rhs_tt = &rhs.tts[i];
let ctxt = tt.span().ctxt();
match (&mut tt, rhs_tt) {
@@ -535,7 +534,7 @@ pub fn compile_declarative_macro(
.pop()
.unwrap();
}
- sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+ sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs")
})
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
@@ -628,6 +627,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree)
// after parsing/expansion. we can report every error in every macro this way.
}
+fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool {
+ if seq.separator.is_some() {
+ false
+ } else {
+ let mut is_empty = true;
+ let mut iter = seq.tts.iter().peekable();
+ while let Some(tt) = iter.next() {
+ match tt {
+ mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
+ mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
+ let mut now = t;
+ while let Some(&mbe::TokenTree::Token(
+ next @ Token { kind: DocComment(..), .. },
+ )) = iter.peek()
+ {
+ now = next;
+ iter.next();
+ }
+ let span = t.span.to(now.span);
+ sess.span_diagnostic.span_note_without_error(
+ span,
+ "doc comments are ignored in matcher position",
+ );
+ }
+ mbe::TokenTree::Sequence(_, sub_seq)
+ if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
+ || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {}
+ _ => is_empty = false,
+ }
+ }
+ is_empty
+ }
+}
+
/// Checks that the lhs contains no repetition which could match an empty token
/// tree, because then the matcher would hang indefinitely.
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
@@ -644,16 +677,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
}
}
TokenTree::Sequence(span, seq) => {
- if seq.separator.is_none()
- && seq.tts.iter().all(|seq_tt| match seq_tt {
- TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
- TokenTree::Sequence(_, sub_seq) => {
- sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
- || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
- }
- _ => false,
- })
- {
+ if is_empty_token_tree(sess, seq) {
let sp = span.entire();
sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
return false;
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index b2bdf9c7e..40bfa3715 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -9,7 +9,7 @@ use rustc_session::parse::{feature_err, ParseSess};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::edition::Edition;
-use rustc_span::{Span, SyntaxContext};
+use rustc_span::Span;
const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
`ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \
@@ -72,7 +72,7 @@ pub(super) fn parse(
// `SyntaxContext::root()` from a foreign crate will
// have the edition of that crate (which we manually
// retrieve via the `edition` parameter).
- if span.ctxt() == SyntaxContext::root() {
+ if span.ctxt().is_root() {
edition
} else {
span.edition()
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 891e84a2f..ecd231511 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
use pm::bridge::{
server, DelimSpan, Diagnostic, ExpnGlobals, Group, Ident, LitKind, Literal, Punct, TokenTree,
};
-use pm::{Delimiter, Level, LineColumn};
+use pm::{Delimiter, Level};
use rustc_ast as ast;
use rustc_ast::token;
use rustc_ast::tokenstream::{self, Spacing::*, TokenStream};
@@ -648,23 +648,22 @@ impl server::Span for Rustc<'_, '_> {
Range { start: relative_start_pos.0 as usize, end: relative_end_pos.0 as usize }
}
-
- fn start(&mut self, span: Self::Span) -> LineColumn {
- let loc = self.sess().source_map().lookup_char_pos(span.lo());
- LineColumn { line: loc.line, column: loc.col.to_usize() }
+ fn start(&mut self, span: Self::Span) -> Self::Span {
+ span.shrink_to_lo()
}
- fn end(&mut self, span: Self::Span) -> LineColumn {
- let loc = self.sess().source_map().lookup_char_pos(span.hi());
- LineColumn { line: loc.line, column: loc.col.to_usize() }
+ fn end(&mut self, span: Self::Span) -> Self::Span {
+ span.shrink_to_hi()
}
- fn before(&mut self, span: Self::Span) -> Self::Span {
- span.shrink_to_lo()
+ fn line(&mut self, span: Self::Span) -> usize {
+ let loc = self.sess().source_map().lookup_char_pos(span.lo());
+ loc.line
}
- fn after(&mut self, span: Self::Span) -> Self::Span {
- span.shrink_to_hi()
+ fn column(&mut self, span: Self::Span) -> usize {
+ let loc = self.sess().source_map().lookup_char_pos(span.lo());
+ loc.col.to_usize() + 1
}
fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index dde9890df..56a2c5eff 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -351,8 +351,6 @@ declare_features! (
(active, const_async_blocks, "1.53.0", Some(85368), None),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003), None),
- /// Allows limiting the evaluation steps of const expressions
- (active, const_eval_limit, "1.43.0", Some(67217), None),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(active, const_extern_fn, "1.40.0", Some(64926), None),
/// Allows basic arithmetic on floating point types in a `const fn`.
@@ -393,10 +391,14 @@ declare_features! (
(active, doc_masked, "1.21.0", Some(44027), None),
/// Allows `dyn* Trait` objects.
(incomplete, dyn_star, "1.65.0", Some(102425), None),
+ // Uses generic effect parameters for ~const bounds
+ (active, effects, "1.72.0", Some(102090), None),
/// Allows `X..Y` patterns.
(active, exclusive_range_pattern, "1.11.0", Some(37854), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(active, exhaustive_patterns, "1.13.0", Some(51085), None),
+ /// Allows explicit tail calls via `become` expression.
+ (incomplete, explicit_tail_calls, "1.72.0", Some(112788), None),
/// Allows using `efiapi`, `sysv64` and `win64` as calling convention
/// for functions with varargs.
(active, extended_varargs_abi_support, "1.65.0", Some(100189), None),
@@ -442,6 +444,8 @@ declare_features! (
(active, intra_doc_pointers, "1.51.0", Some(80896), None),
// Allows setting the threshold for the `large_assignments` lint.
(active, large_assignments, "1.52.0", Some(83518), None),
+ /// Allow to have type alias types for inter-crate use.
+ (active, lazy_type_alias, "1.72.0", Some(112792), None),
/// Allows `if/while p && let q = r && ...` chains.
(active, let_chains, "1.37.0", Some(53667), None),
/// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check.
@@ -541,6 +545,8 @@ declare_features! (
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(active, type_changing_struct_update, "1.58.0", Some(86555), None),
+ /// Allows using type privacy lints (`private_interfaces`, `private_bounds`, `unnameable_types`).
+ (active, type_privacy_lints, "1.72.0", Some(48054), None),
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
(active, unix_sigpipe, "1.65.0", Some(97889), None),
/// Allows unsized fn parameters.
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 06f4a0b5e..366000044 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -356,10 +356,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
- const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
- const_eval_limit, experimental!(const_eval_limit)
- ),
- gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)
),
@@ -709,7 +705,11 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
rustc_attr!(
- rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+ rustc_deny_explicit_impl,
+ AttributeType::Normal,
+ template!(List: "implement_via_object = (true|false)"),
+ ErrorFollowing,
+ @only_local: true,
"#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
),
rustc_attr!(
@@ -723,6 +723,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
and it is only intended to be used in `alloc`."
),
+ rustc_attr!(
+ rustc_host, AttributeType::Normal, template!(Word), ErrorFollowing,
+ "#[rustc_host] annotates const generic parameters as the `host` effect param, \
+ and it is only intended for internal use and as a desugaring."
+ ),
+
BuiltinAttribute {
name: sym::rustc_diagnostic_item,
// FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index 8bca24b2b..ed5d76b86 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -59,8 +59,10 @@ declare_features! (
/// Allows comparing raw pointers during const eval.
(removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
Some("cannot be allowed in const eval in any meaningful way")),
+ /// Allows limiting the evaluation steps of const expressions
+ (removed, const_eval_limit, "1.43.0", Some(67217), None, Some("removed the limit entirely")),
/// Allows non-trivial generic constants which have to be manually propagated upwards.
- (removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")),
+ (removed, const_evaluatable_checked, "1.48.0", Some(76560), None, Some("renamed to `generic_const_exprs`")),
/// Allows the definition of `const` functions with some advanced features.
(removed, const_fn, "1.54.0", Some(57563), None,
Some("split into finer-grained feature gates")),
diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs
index 9dffc9a76..56e23ac27 100644
--- a/compiler/rustc_fluent_macro/src/fluent.rs
+++ b/compiler/rustc_fluent_macro/src/fluent.rs
@@ -179,7 +179,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
let mut previous_defns = HashMap::new();
let mut message_refs = Vec::new();
for entry in resource.entries() {
- if let Entry::Message(Message { id: Identifier { name }, attributes, value, .. }) = entry {
+ if let Entry::Message(msg) = entry {
+ let Message { id: Identifier { name }, attributes, value, .. } = msg;
let _ = previous_defns.entry(name.to_string()).or_insert(resource_span);
if name.contains('-') {
Diagnostic::spanned(
@@ -229,9 +230,10 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
continue;
}
- let msg = format!("Constant referring to Fluent message `{name}` from `{crate_name}`");
+ let docstr =
+ format!("Constant referring to Fluent message `{name}` from `{crate_name}`");
constants.extend(quote! {
- #[doc = #msg]
+ #[doc = #docstr]
pub const #snake_name: crate::DiagnosticMessage =
crate::DiagnosticMessage::FluentIdentifier(
std::borrow::Cow::Borrowed(#name),
@@ -269,6 +271,15 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
);
});
}
+
+ // Record variables referenced by these messages so we can produce
+ // tests in the derive diagnostics to validate them.
+ let ident = quote::format_ident!("{snake_name}_refs");
+ let vrefs = variable_references(msg);
+ constants.extend(quote! {
+ #[cfg(test)]
+ pub const #ident: &[&str] = &[#(#vrefs),*];
+ })
}
}
@@ -334,3 +345,28 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
}
.into()
}
+
+fn variable_references<'a>(msg: &Message<&'a str>) -> Vec<&'a str> {
+ let mut refs = vec![];
+ if let Some(Pattern { elements }) = &msg.value {
+ for elt in elements {
+ if let PatternElement::Placeable {
+ expression: Expression::Inline(InlineExpression::VariableReference { id }),
+ } = elt
+ {
+ refs.push(id.name);
+ }
+ }
+ }
+ for attr in &msg.attributes {
+ for elt in &attr.value.elements {
+ if let PatternElement::Placeable {
+ expression: Expression::Inline(InlineExpression::VariableReference { id }),
+ } = elt
+ {
+ refs.push(id.name);
+ }
+ }
+ }
+ refs
+}
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index e84473109..6c419471d 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1675,6 +1675,14 @@ pub struct AnonConst {
pub body: BodyId,
}
+/// An inline constant expression `const { something }`.
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
+pub struct ConstBlock {
+ pub hir_id: HirId,
+ pub def_id: LocalDefId,
+ pub body: BodyId,
+}
+
/// An expression.
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub struct Expr<'hir> {
@@ -1711,6 +1719,7 @@ impl Expr<'_> {
ExprKind::Break(..) => ExprPrecedence::Break,
ExprKind::Continue(..) => ExprPrecedence::Continue,
ExprKind::Ret(..) => ExprPrecedence::Ret,
+ ExprKind::Become(..) => ExprPrecedence::Become,
ExprKind::InlineAsm(..) => ExprPrecedence::InlineAsm,
ExprKind::OffsetOf(..) => ExprPrecedence::OffsetOf,
ExprKind::Struct(..) => ExprPrecedence::Struct,
@@ -1768,6 +1777,7 @@ impl Expr<'_> {
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
+ | ExprKind::Become(..)
| ExprKind::Let(..)
| ExprKind::Loop(..)
| ExprKind::Assign(..)
@@ -1858,6 +1868,7 @@ impl Expr<'_> {
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
+ | ExprKind::Become(..)
| ExprKind::Let(..)
| ExprKind::Loop(..)
| ExprKind::Assign(..)
@@ -1922,7 +1933,7 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool {
#[derive(Debug, Clone, Copy, HashStable_Generic)]
pub enum ExprKind<'hir> {
/// Allow anonymous constants from an inline `const` block
- ConstBlock(AnonConst),
+ ConstBlock(ConstBlock),
/// An array (e.g., `[a, b, c, d]`).
Array(&'hir [Expr<'hir>]),
/// A function call.
@@ -2017,6 +2028,8 @@ pub enum ExprKind<'hir> {
Continue(Destination),
/// A `return`, with an optional value to be returned.
Ret(Option<&'hir Expr<'hir>>),
+ /// A `become`, with the value to be returned.
+ Become(&'hir Expr<'hir>),
/// Inline assembly (from `asm!`), with its outputs and inputs.
InlineAsm(&'hir InlineAsm<'hir>),
@@ -2651,6 +2664,10 @@ pub struct OpaqueTy<'hir> {
pub generics: &'hir Generics<'hir>,
pub bounds: GenericBounds<'hir>,
pub origin: OpaqueTyOrigin,
+ // Opaques have duplicated lifetimes, this mapping connects the original lifetime with the copy
+ // so we can later generate bidirectional outlives predicates to enforce that these lifetimes
+ // stay in sync.
+ pub lifetime_mapping: &'hir [(Lifetime, LocalDefId)],
pub in_trait: bool,
}
@@ -3302,7 +3319,7 @@ pub enum ItemKind<'hir> {
/// A type alias, e.g., `type Foo = Bar<u8>`.
TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>),
/// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.
- OpaqueTy(OpaqueTy<'hir>),
+ OpaqueTy(&'hir OpaqueTy<'hir>),
/// An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`.
Enum(EnumDef<'hir>, &'hir Generics<'hir>),
/// A struct definition, e.g., `struct Foo<A> {x: A}`.
@@ -3641,6 +3658,7 @@ pub enum Node<'hir> {
Variant(&'hir Variant<'hir>),
Field(&'hir FieldDef<'hir>),
AnonConst(&'hir AnonConst),
+ ConstBlock(&'hir ConstBlock),
Expr(&'hir Expr<'hir>),
ExprField(&'hir ExprField<'hir>),
Stmt(&'hir Stmt<'hir>),
@@ -3695,6 +3713,7 @@ impl<'hir> Node<'hir> {
Node::TypeBinding(b) => Some(b.ident),
Node::Param(..)
| Node::AnonConst(..)
+ | Node::ConstBlock(..)
| Node::Expr(..)
| Node::Stmt(..)
| Node::Block(..)
@@ -3733,6 +3752,29 @@ impl<'hir> Node<'hir> {
}
}
+ /// Get the type for constants, assoc types, type aliases and statics.
+ pub fn ty(self) -> Option<&'hir Ty<'hir>> {
+ match self {
+ Node::Item(it) => match it.kind {
+ ItemKind::TyAlias(ty, _) | ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => {
+ Some(ty)
+ }
+ _ => None,
+ },
+ Node::TraitItem(it) => match it.kind {
+ TraitItemKind::Const(ty, _) => Some(ty),
+ TraitItemKind::Type(_, ty) => ty,
+ _ => None,
+ },
+ Node::ImplItem(it) => match it.kind {
+ ImplItemKind::Const(ty, _) => Some(ty),
+ ImplItemKind::Type(ty) => Some(ty),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+
pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> {
match self {
Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty),
@@ -3758,7 +3800,7 @@ impl<'hir> Node<'hir> {
})
| Node::Expr(Expr {
kind:
- ExprKind::ConstBlock(AnonConst { body, .. })
+ ExprKind::ConstBlock(ConstBlock { body, .. })
| ExprKind::Closure(Closure { body, .. })
| ExprKind::Repeat(_, ArrayLen::Body(AnonConst { body, .. })),
..
@@ -3878,6 +3920,13 @@ impl<'hir> Node<'hir> {
this
}
+ /// Expect a [`Node::ConstBlock`] or panic.
+ #[track_caller]
+ pub fn expect_inline_const(self) -> &'hir ConstBlock {
+ let Node::ConstBlock(this) = self else { self.expect_failed("an inline constant") };
+ this
+ }
+
/// Expect a [`Node::Expr`] or panic.
#[track_caller]
pub fn expect_expr(self) -> &'hir Expr<'hir> {
diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs
index d549f52f8..34c615779 100644
--- a/compiler/rustc_hir/src/hir_id.rs
+++ b/compiler/rustc_hir/src/hir_id.rs
@@ -166,7 +166,9 @@ impl ItemLocalId {
// Safety: Ord is implement as just comparing the ItemLocalId's numerical
// values and these are not changed by (de-)serialization.
-unsafe impl StableOrd for ItemLocalId {}
+unsafe impl StableOrd for ItemLocalId {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.
pub const CRATE_HIR_ID: HirId =
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index df0047d82..347c1f463 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -335,6 +335,9 @@ pub trait Visitor<'v>: Sized {
fn visit_anon_const(&mut self, c: &'v AnonConst) {
walk_anon_const(self, c)
}
+ fn visit_inline_const(&mut self, c: &'v ConstBlock) {
+ walk_inline_const(self, c)
+ }
fn visit_expr(&mut self, ex: &'v Expr<'v>) {
walk_expr(self, ex)
}
@@ -499,7 +502,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
visitor.visit_ty(ty);
visitor.visit_generics(generics)
}
- ItemKind::OpaqueTy(OpaqueTy { ref generics, bounds, .. }) => {
+ ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
visitor.visit_id(item.hir_id());
walk_generics(visitor, generics);
walk_list!(visitor, visit_param_bound, bounds);
@@ -679,13 +682,18 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo
visitor.visit_nested_body(constant.body);
}
+pub fn walk_inline_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v ConstBlock) {
+ visitor.visit_id(constant.hir_id);
+ visitor.visit_nested_body(constant.body);
+}
+
pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
visitor.visit_id(expression.hir_id);
match expression.kind {
ExprKind::Array(subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions);
}
- ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const),
+ ExprKind::ConstBlock(ref const_block) => visitor.visit_inline_const(const_block),
ExprKind::Repeat(ref element, ref count) => {
visitor.visit_expr(element);
visitor.visit_array_length(count)
@@ -783,6 +791,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
ExprKind::Ret(ref optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
+ ExprKind::Become(ref expr) => visitor.visit_expr(expr),
ExprKind::InlineAsm(ref asm) => {
visitor.visit_inline_asm(asm, expression.hir_id);
}
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 4b3bc816b..302a94984 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -250,7 +250,6 @@ language_item_table! {
FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
- BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
@@ -260,6 +259,8 @@ language_item_table! {
EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
+ // Experimental language item for Miri
+ PtrUnique, sym::ptr_unique, ptr_unique, Target::Struct, GenericRequirement::Exact(1);
PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 02d1dfcd1..166760166 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -99,6 +99,15 @@ hir_analysis_invalid_union_field =
hir_analysis_invalid_union_field_sugg =
wrap the field type in `ManuallyDrop<...>`
+hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
+ .label = const parameter declared here
+
+hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetimes from an fn or impl
+ .label = lifetime declared here
+
+hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
+ .label = type parameter declared here
+
hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
.label = lifetimes do not match {$item_kind} in trait
@@ -137,7 +146,7 @@ hir_analysis_missing_trait_item_suggestion = implement the missing item: `{$snip
hir_analysis_missing_trait_item_unstable = not all trait items implemented, missing: `{$missing_item_name}`
.note = default implementation of `{$missing_item_name}` is unstable
- .some_note = use of unstable library feature '{$feature}': {$r}
+ .some_note = use of unstable library feature '{$feature}': {$reason}
.none_note = use of unstable library feature '{$feature}'
hir_analysis_missing_type_params =
@@ -195,6 +204,13 @@ hir_analysis_return_type_notation_conflicting_bound =
hir_analysis_return_type_notation_equality_bound =
return type notation is not allowed to use type equality
+hir_analysis_return_type_notation_illegal_param_const =
+ return type notation is not allowed for functions that have const parameters
+ .label = const parameter declared here
+hir_analysis_return_type_notation_illegal_param_type =
+ return type notation is not allowed for functions that have type parameters
+ .label = type parameter declared here
+
hir_analysis_return_type_notation_missing_method =
cannot find associated function `{$assoc_name}` for `{$ty_name}`
@@ -232,6 +248,9 @@ hir_analysis_static_specialize = cannot specialize on `'static` lifetime
hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
+hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature
+ .note = this item must mention the opaque type in its signature in order to be able to register hidden types
+
hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]`
hir_analysis_too_large_static = extern static is too large for the current architecture
@@ -272,6 +291,11 @@ hir_analysis_unrecognized_intrinsic_function =
unrecognized intrinsic function: `{$name}`
.label = unrecognized intrinsic
+hir_analysis_unused_associated_type_bounds =
+ unnecessary associated type bound for not object safe associated type
+ .note = this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`.
+ .suggestion = remove this bound
+
hir_analysis_value_of_associated_struct_already_specified =
the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified
.label = re-bound here
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
new file mode 100644
index 000000000..b13de7701
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -0,0 +1,575 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_lint_defs::Applicability;
+use rustc_middle::ty::{self as ty, Ty, TypeVisitableExt};
+use rustc_span::symbol::Ident;
+use rustc_span::{ErrorGuaranteed, Span};
+use rustc_trait_selection::traits;
+
+use crate::astconv::{
+ AstConv, ConvertedBinding, ConvertedBindingKind, OnlySelfBounds, PredicateFilter,
+};
+use crate::bounds::Bounds;
+use crate::errors::{MultipleRelaxedDefaultBounds, ValueOfAssociatedStructAlreadySpecified};
+
+impl<'tcx> dyn AstConv<'tcx> + '_ {
+ /// Sets `implicitly_sized` to true on `Bounds` if necessary
+ pub(crate) fn add_implicitly_sized(
+ &self,
+ bounds: &mut Bounds<'tcx>,
+ self_ty: Ty<'tcx>,
+ ast_bounds: &'tcx [hir::GenericBound<'tcx>],
+ self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
+ span: Span,
+ ) {
+ let tcx = self.tcx();
+
+ // Try to find an unbound in bounds.
+ let mut unbound = None;
+ let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
+ for ab in ast_bounds {
+ if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
+ if unbound.is_none() {
+ unbound = Some(&ptr.trait_ref);
+ } else {
+ tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
+ }
+ }
+ }
+ };
+ search_bounds(ast_bounds);
+ if let Some((self_ty, where_clause)) = self_ty_where_predicates {
+ for clause in where_clause {
+ if let hir::WherePredicate::BoundPredicate(pred) = clause {
+ if pred.is_param_bound(self_ty.to_def_id()) {
+ search_bounds(pred.bounds);
+ }
+ }
+ }
+ }
+
+ let sized_def_id = tcx.lang_items().sized_trait();
+ match (&sized_def_id, unbound) {
+ (Some(sized_def_id), Some(tpb))
+ if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
+ {
+ // There was in fact a `?Sized` bound, return without doing anything
+ return;
+ }
+ (_, Some(_)) => {
+ // There was a `?Trait` bound, but it was not `?Sized`; warn.
+ tcx.sess.span_warn(
+ span,
+ "default bound relaxed for a type parameter, but \
+ this does nothing because the given bound is not \
+ a default; only `?Sized` is supported",
+ );
+ // Otherwise, add implicitly sized if `Sized` is available.
+ }
+ _ => {
+ // There was no `?Sized` bound; add implicitly sized if `Sized` is available.
+ }
+ }
+ if sized_def_id.is_none() {
+ // No lang item for `Sized`, so we can't add it as a bound.
+ return;
+ }
+ bounds.push_sized(tcx, self_ty, span);
+ }
+
+ /// This helper takes a *converted* parameter type (`param_ty`)
+ /// and an *unconverted* list of bounds:
+ ///
+ /// ```text
+ /// fn foo<T: Debug>
+ /// ^ ^^^^^ `ast_bounds` parameter, in HIR form
+ /// |
+ /// `param_ty`, in ty form
+ /// ```
+ ///
+ /// It adds these `ast_bounds` into the `bounds` structure.
+ ///
+ /// **A note on binders:** there is an implied binder around
+ /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
+ /// for more details.
+ #[instrument(level = "debug", skip(self, ast_bounds, bounds))]
+ pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: I,
+ bounds: &mut Bounds<'tcx>,
+ bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+ only_self_bounds: OnlySelfBounds,
+ ) {
+ for ast_bound in ast_bounds {
+ match ast_bound {
+ hir::GenericBound::Trait(poly_trait_ref, modifier) => {
+ let (constness, polarity) = match modifier {
+ hir::TraitBoundModifier::MaybeConst => {
+ (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
+ }
+ hir::TraitBoundModifier::None => {
+ (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
+ }
+ hir::TraitBoundModifier::Negative => {
+ (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
+ }
+ hir::TraitBoundModifier::Maybe => continue,
+ };
+ let _ = self.instantiate_poly_trait_ref(
+ &poly_trait_ref.trait_ref,
+ poly_trait_ref.span,
+ constness,
+ polarity,
+ param_ty,
+ bounds,
+ false,
+ only_self_bounds,
+ );
+ }
+ &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
+ self.instantiate_lang_item_trait_ref(
+ lang_item,
+ span,
+ hir_id,
+ args,
+ param_ty,
+ bounds,
+ only_self_bounds,
+ );
+ }
+ hir::GenericBound::Outlives(lifetime) => {
+ let region = self.ast_region_to_region(lifetime, None);
+ bounds.push_region_bound(
+ self.tcx(),
+ ty::Binder::bind_with_vars(
+ ty::OutlivesPredicate(param_ty, region),
+ bound_vars,
+ ),
+ lifetime.ident.span,
+ );
+ }
+ }
+ }
+ }
+
+ /// Translates a list of bounds from the HIR into the `Bounds` data structure.
+ /// The self-type for the bounds is given by `param_ty`.
+ ///
+ /// Example:
+ ///
+ /// ```ignore (illustrative)
+ /// fn foo<T: Bar + Baz>() { }
+ /// // ^ ^^^^^^^^^ ast_bounds
+ /// // param_ty
+ /// ```
+ ///
+ /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
+ /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
+ /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
+ ///
+ /// `span` should be the declaration size of the parameter.
+ pub(crate) fn compute_bounds(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: &[hir::GenericBound<'_>],
+ filter: PredicateFilter,
+ ) -> Bounds<'tcx> {
+ let mut bounds = Bounds::default();
+
+ let only_self_bounds = match filter {
+ PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
+ OnlySelfBounds(false)
+ }
+ PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => OnlySelfBounds(true),
+ };
+
+ self.add_bounds(
+ param_ty,
+ ast_bounds.iter().filter(|bound| {
+ match filter {
+ PredicateFilter::All
+ | PredicateFilter::SelfOnly
+ | PredicateFilter::SelfAndAssociatedTypeBounds => true,
+ PredicateFilter::SelfThatDefines(assoc_name) => {
+ if let Some(trait_ref) = bound.trait_ref()
+ && let Some(trait_did) = trait_ref.trait_def_id()
+ && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
+ {
+ true
+ } else {
+ false
+ }
+ }
+ }
+ }),
+ &mut bounds,
+ ty::List::empty(),
+ only_self_bounds,
+ );
+ debug!(?bounds);
+
+ bounds
+ }
+
+ /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
+ /// onto `bounds`.
+ ///
+ /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
+ /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
+ /// the binder (e.g., `&'a u32`) and hence may reference bound regions.
+ #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
+ pub(super) fn add_predicates_for_ast_type_binding(
+ &self,
+ hir_ref_id: hir::HirId,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ binding: &ConvertedBinding<'_, 'tcx>,
+ bounds: &mut Bounds<'tcx>,
+ speculative: bool,
+ dup_bindings: &mut FxHashMap<DefId, Span>,
+ path_span: Span,
+ constness: ty::BoundConstness,
+ only_self_bounds: OnlySelfBounds,
+ polarity: ty::ImplPolarity,
+ ) -> Result<(), ErrorGuaranteed> {
+ // Given something like `U: SomeTrait<T = X>`, we want to produce a
+ // predicate like `<U as SomeTrait>::T = X`. This is somewhat
+ // subtle in the event that `T` is defined in a supertrait of
+ // `SomeTrait`, because in that case we need to upcast.
+ //
+ // That is, consider this case:
+ //
+ // ```
+ // trait SubTrait: SuperTrait<i32> { }
+ // trait SuperTrait<A> { type T; }
+ //
+ // ... B: SubTrait<T = foo> ...
+ // ```
+ //
+ // We want to produce `<B as SuperTrait<i32>>::T == foo`.
+
+ let tcx = self.tcx();
+
+ let return_type_notation =
+ binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
+
+ let candidate = if return_type_notation {
+ if self.trait_defines_associated_item_named(
+ trait_ref.def_id(),
+ ty::AssocKind::Fn,
+ binding.item_name,
+ ) {
+ trait_ref
+ } else {
+ self.one_bound_for_assoc_method(
+ traits::supertraits(tcx, trait_ref),
+ trait_ref.print_only_trait_path(),
+ binding.item_name,
+ path_span,
+ )?
+ }
+ } else if self.trait_defines_associated_item_named(
+ trait_ref.def_id(),
+ ty::AssocKind::Type,
+ binding.item_name,
+ ) {
+ // Simple case: X is defined in the current trait.
+ trait_ref
+ } else {
+ // Otherwise, we have to walk through the supertraits to find
+ // those that do.
+ self.one_bound_for_assoc_type(
+ || traits::supertraits(tcx, trait_ref),
+ trait_ref.skip_binder().print_only_trait_name(),
+ binding.item_name,
+ path_span,
+ match binding.kind {
+ ConvertedBindingKind::Equality(term) => Some(term),
+ _ => None,
+ },
+ )?
+ };
+
+ let (assoc_ident, def_scope) =
+ tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
+
+ // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
+ // of calling `filter_by_name_and_kind`.
+ let find_item_of_kind = |kind| {
+ tcx.associated_items(candidate.def_id())
+ .filter_by_name_unhygienic(assoc_ident.name)
+ .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
+ };
+ let assoc_item = if return_type_notation {
+ find_item_of_kind(ty::AssocKind::Fn)
+ } else {
+ find_item_of_kind(ty::AssocKind::Type)
+ .or_else(|| find_item_of_kind(ty::AssocKind::Const))
+ }
+ .expect("missing associated type");
+
+ if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
+ tcx.sess
+ .struct_span_err(
+ binding.span,
+ format!("{} `{}` is private", assoc_item.kind, binding.item_name),
+ )
+ .span_label(binding.span, format!("private {}", assoc_item.kind))
+ .emit();
+ }
+ tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
+
+ if !speculative {
+ dup_bindings
+ .entry(assoc_item.def_id)
+ .and_modify(|prev_span| {
+ tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
+ span: binding.span,
+ prev_span: *prev_span,
+ item_name: binding.item_name,
+ def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
+ });
+ })
+ .or_insert(binding.span);
+ }
+
+ let projection_ty = if return_type_notation {
+ let mut emitted_bad_param_err = false;
+ // If we have an method return type bound, then we need to substitute
+ // the method's early bound params with suitable late-bound params.
+ let mut num_bound_vars = candidate.bound_vars().len();
+ let substs =
+ candidate.skip_binder().substs.extend_to(tcx, assoc_item.def_id, |param, _| {
+ let subst = match param.kind {
+ ty::GenericParamDefKind::Lifetime => ty::Region::new_late_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundRegion {
+ var: ty::BoundVar::from_usize(num_bound_vars),
+ kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name),
+ },
+ )
+ .into(),
+ ty::GenericParamDefKind::Type { .. } => {
+ if !emitted_bad_param_err {
+ tcx.sess.emit_err(
+ crate::errors::ReturnTypeNotationIllegalParam::Type {
+ span: path_span,
+ param_span: tcx.def_span(param.def_id),
+ },
+ );
+ emitted_bad_param_err = true;
+ }
+ Ty::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundTy {
+ var: ty::BoundVar::from_usize(num_bound_vars),
+ kind: ty::BoundTyKind::Param(param.def_id, param.name),
+ },
+ )
+ .into()
+ }
+ ty::GenericParamDefKind::Const { .. } => {
+ if !emitted_bad_param_err {
+ tcx.sess.emit_err(
+ crate::errors::ReturnTypeNotationIllegalParam::Const {
+ span: path_span,
+ param_span: tcx.def_span(param.def_id),
+ },
+ );
+ emitted_bad_param_err = true;
+ }
+ let ty = tcx
+ .type_of(param.def_id)
+ .no_bound_vars()
+ .expect("ct params cannot have early bound vars");
+ ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(num_bound_vars),
+ ty,
+ )
+ .into()
+ }
+ };
+ num_bound_vars += 1;
+ subst
+ });
+
+ // Next, we need to check that the return-type notation is being used on
+ // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
+ let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output();
+ let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
+ && tcx.is_impl_trait_in_trait(alias_ty.def_id)
+ {
+ alias_ty
+ } else {
+ return Err(self.tcx().sess.emit_err(
+ crate::errors::ReturnTypeNotationOnNonRpitit {
+ span: binding.span,
+ ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
+ fn_span: tcx.hir().span_if_local(assoc_item.def_id),
+ note: (),
+ },
+ ));
+ };
+
+ // Finally, move the fn return type's bound vars over to account for the early bound
+ // params (and trait ref's late bound params). This logic is very similar to
+ // `Predicate::subst_supertrait`, and it's no coincidence why.
+ let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
+ let subst_output = ty::EarlyBinder::bind(shifted_output).subst(tcx, substs);
+
+ let bound_vars = tcx.late_bound_vars(binding.hir_id);
+ ty::Binder::bind_with_vars(subst_output, bound_vars)
+ } else {
+ // Include substitutions for generic parameters of associated types
+ candidate.map_bound(|trait_ref| {
+ let ident = Ident::new(assoc_item.name, binding.item_name.span);
+ let item_segment = hir::PathSegment {
+ ident,
+ hir_id: binding.hir_id,
+ res: Res::Err,
+ args: Some(binding.gen_args),
+ infer_args: false,
+ };
+
+ let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
+ path_span,
+ assoc_item.def_id,
+ &item_segment,
+ trait_ref.substs,
+ );
+
+ debug!(?substs_trait_ref_and_assoc_item);
+
+ tcx.mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
+ })
+ };
+
+ if !speculative {
+ // Find any late-bound regions declared in `ty` that are not
+ // declared in the trait-ref or assoc_item. These are not well-formed.
+ //
+ // Example:
+ //
+ // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
+ // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
+ if let ConvertedBindingKind::Equality(ty) = binding.kind {
+ let late_bound_in_trait_ref =
+ tcx.collect_constrained_late_bound_regions(&projection_ty);
+ let late_bound_in_ty =
+ tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
+ debug!(?late_bound_in_trait_ref);
+ debug!(?late_bound_in_ty);
+
+ // FIXME: point at the type params that don't have appropriate lifetimes:
+ // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
+ // ---- ---- ^^^^^^^
+ self.validate_late_bound_regions(
+ late_bound_in_trait_ref,
+ late_bound_in_ty,
+ |br_name| {
+ struct_span_err!(
+ tcx.sess,
+ binding.span,
+ E0582,
+ "binding for associated type `{}` references {}, \
+ which does not appear in the trait input types",
+ binding.item_name,
+ br_name
+ )
+ },
+ );
+ }
+ }
+
+ match binding.kind {
+ ConvertedBindingKind::Equality(..) if return_type_notation => {
+ return Err(self.tcx().sess.emit_err(
+ crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
+ ));
+ }
+ ConvertedBindingKind::Equality(mut term) => {
+ // "Desugar" a constraint like `T: Iterator<Item = u32>` this to
+ // the "projection predicate" for:
+ //
+ // `<T as Iterator>::Item = u32`
+ let assoc_item_def_id = projection_ty.skip_binder().def_id;
+ let def_kind = tcx.def_kind(assoc_item_def_id);
+ match (def_kind, term.unpack()) {
+ (hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
+ | (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
+ (_, _) => {
+ let got = if let Some(_) = term.ty() { "type" } else { "constant" };
+ let expected = tcx.def_descr(assoc_item_def_id);
+ let mut err = tcx.sess.struct_span_err(
+ binding.span,
+ format!("expected {expected} bound, found {got}"),
+ );
+ err.span_note(
+ tcx.def_span(assoc_item_def_id),
+ format!("{expected} defined here"),
+ );
+
+ if let hir::def::DefKind::AssocConst = def_kind
+ && let Some(t) = term.ty() && (t.is_enum() || t.references_error())
+ && tcx.features().associated_const_equality {
+ err.span_suggestion(
+ binding.span,
+ "if equating a const, try wrapping with braces",
+ format!("{} = {{ const }}", binding.item_name),
+ Applicability::HasPlaceholders,
+ );
+ }
+ let reported = err.emit();
+ term = match def_kind {
+ hir::def::DefKind::AssocTy => Ty::new_error(tcx, reported).into(),
+ hir::def::DefKind::AssocConst => ty::Const::new_error(
+ tcx,
+ reported,
+ tcx.type_of(assoc_item_def_id)
+ .subst(tcx, projection_ty.skip_binder().substs),
+ )
+ .into(),
+ _ => unreachable!(),
+ };
+ }
+ }
+ bounds.push_projection_bound(
+ tcx,
+ projection_ty
+ .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
+ binding.span,
+ );
+ }
+ ConvertedBindingKind::Constraint(ast_bounds) => {
+ // "Desugar" a constraint like `T: Iterator<Item: Debug>` to
+ //
+ // `<T as Iterator>::Item: Debug`
+ //
+ // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
+ // parameter to have a skipped binder.
+ //
+ // NOTE: If `only_self_bounds` is true, do NOT expand this associated
+ // type bound into a trait predicate, since we only want to add predicates
+ // for the `Self` type.
+ if !only_self_bounds.0 {
+ let param_ty = Ty::new_alias(tcx, ty::Projection, projection_ty.skip_binder());
+ self.add_bounds(
+ param_ty,
+ ast_bounds.iter(),
+ bounds,
+ projection_ty.bound_vars(),
+ only_self_bounds,
+ );
+ }
+ }
+ }
+ Ok(())
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index 7b922f5d5..ddf99853b 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -122,9 +122,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let all_candidate_names: Vec<_> = all_candidates()
.flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
- .filter_map(
- |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
- )
+ .filter_map(|item| {
+ if item.opt_rpitit_info.is_none() && item.kind == ty::AssocKind::Type {
+ Some(item.name)
+ } else {
+ None
+ }
+ })
.collect();
if let (Some(suggested_name), true) = (
@@ -159,9 +163,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.flat_map(|trait_def_id| {
self.tcx().associated_items(*trait_def_id).in_definition_order()
})
- .filter_map(
- |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
- )
+ .filter_map(|item| {
+ if item.opt_rpitit_info.is_none() && item.kind == ty::AssocKind::Type {
+ Some(item.name)
+ } else {
+ None
+ }
+ })
.collect();
if let (Some(suggested_name), true) = (
@@ -343,13 +351,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let format_pred = |pred: ty::Predicate<'tcx>| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`.
let projection_ty = pred.skip_binder().projection_ty;
let substs_with_infer_self = tcx.mk_substs_from_iter(
- std::iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+ std::iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
.chain(projection_ty.substs.iter().skip(1)),
);
@@ -364,7 +372,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
Some((obligation, projection_ty.self_ty()))
}
- ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => {
let p = poly_trait_ref.trait_ref;
let self_ty = p.self_ty();
let path = p.print_only_trait_path();
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
new file mode 100644
index 000000000..05a3ab63d
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -0,0 +1,124 @@
+use rustc_ast::TraitObjectSyntax;
+use rustc_errors::{Diagnostic, StashKey};
+use rustc_hir as hir;
+use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
+use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
+
+use super::AstConv;
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+ /// Make sure that we are in the condition to suggest the blanket implementation.
+ pub(super) fn maybe_lint_blanket_trait_impl(
+ &self,
+ self_ty: &hir::Ty<'_>,
+ diag: &mut Diagnostic,
+ ) {
+ let tcx = self.tcx();
+ let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
+ if let hir::Node::Item(hir::Item {
+ kind:
+ hir::ItemKind::Impl(hir::Impl {
+ self_ty: impl_self_ty, of_trait: Some(of_trait_ref), generics, ..
+ }),
+ ..
+ }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id
+ {
+ if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
+ return;
+ }
+ let of_trait_span = of_trait_ref.path.span;
+ // make sure that we are not calling unwrap to abort during the compilation
+ let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return; };
+ let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; };
+ // check if the trait has generics, to make a correct suggestion
+ let param_name = generics.params.next_type_param_name(None);
+
+ let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
+ (span, format!(", {}: {}", param_name, impl_trait_name))
+ } else {
+ (generics.span, format!("<{}: {}>", param_name, impl_trait_name))
+ };
+ diag.multipart_suggestion(
+ format!("alternatively use a blanket \
+ implementation to implement `{of_trait_name}` for \
+ all types that also implement `{impl_trait_name}`"),
+ vec![
+ (self_ty.span, param_name),
+ add_generic_sugg,
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
+ pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
+ let tcx = self.tcx();
+ if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
+ self_ty.kind
+ {
+ let needs_bracket = in_path
+ && !tcx
+ .sess
+ .source_map()
+ .span_to_prev_source(self_ty.span)
+ .ok()
+ .is_some_and(|s| s.trim_end().ends_with('<'));
+
+ let is_global = poly_trait_ref.trait_ref.path.is_global();
+
+ let mut sugg = Vec::from_iter([(
+ self_ty.span.shrink_to_lo(),
+ format!(
+ "{}dyn {}",
+ if needs_bracket { "<" } else { "" },
+ if is_global { "(" } else { "" },
+ ),
+ )]);
+
+ if is_global || needs_bracket {
+ sugg.push((
+ self_ty.span.shrink_to_hi(),
+ format!(
+ "{}{}",
+ if is_global { ")" } else { "" },
+ if needs_bracket { ">" } else { "" },
+ ),
+ ));
+ }
+
+ if self_ty.span.edition().rust_2021() {
+ let msg = "trait objects must include the `dyn` keyword";
+ let label = "add `dyn` keyword before this trait";
+ let mut diag =
+ rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
+ if self_ty.span.can_be_used_for_suggestions() {
+ diag.multipart_suggestion_verbose(
+ label,
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ // check if the impl trait that we are considering is a impl of a local trait
+ self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
+ diag.stash(self_ty.span, StashKey::TraitMissingMethod);
+ } else {
+ let msg = "trait objects without an explicit `dyn` are deprecated";
+ tcx.struct_span_lint_hir(
+ BARE_TRAIT_OBJECTS,
+ self_ty.hir_id,
+ self_ty.span,
+ msg,
+ |lint| {
+ lint.multipart_suggestion_verbose(
+ "use `dyn`",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ self.maybe_lint_blanket_trait_impl(&self_ty, lint);
+ lint
+ },
+ );
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 2c60a0624..3d6984628 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2,53 +2,45 @@
//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an
//! instance of `AstConv`.
+mod bounds;
mod errors;
pub mod generics;
+mod lint;
+mod object_safety;
use crate::astconv::errors::prohibit_assoc_ty_binding;
use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args};
use crate::bounds::Bounds;
use crate::collect::HirPlaceholderCollector;
-use crate::errors::{
- AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
- TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
-};
+use crate::errors::{AmbiguousLifetimeBound, TypeofReservedKeywordUsed};
use crate::middle::resolve_bound_vars as rbv;
use crate::require_c_abi_if_c_variadic;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError,
- MultiSpan, StashKey,
+ MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
-use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
-use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::middle::stability::AllowUnstable;
-use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{DynKind, ToPredicate};
-use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
+use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi;
-use rustc_trait_selection::traits::error_reporting::{
- report_object_safety_error, suggestions::NextTypeParamName,
-};
use rustc_trait_selection::traits::wf::object_region_bounds;
-use rustc_trait_selection::traits::{self, astconv_object_safety_violations, ObligationCtxt};
+use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCtxt};
+use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
-use smallvec::{smallvec, SmallVec};
-use std::collections::BTreeSet;
use std::fmt::Display;
use std::slice;
@@ -58,6 +50,24 @@ pub struct PathSeg(pub DefId, pub usize);
#[derive(Copy, Clone, Debug)]
pub struct OnlySelfBounds(pub bool);
+#[derive(Copy, Clone, Debug)]
+pub enum PredicateFilter {
+ /// All predicates may be implied by the trait.
+ All,
+
+ /// Only traits that reference `Self: ..` are implied by the trait.
+ SelfOnly,
+
+ /// Only traits that reference `Self: ..` and define an associated type
+ /// with the given ident are implied by the trait.
+ SelfThatDefines(Ident),
+
+ /// Only traits that reference `Self: ..` and their associated type bounds.
+ /// For example, given `Self: Tr<A: B>`, this would expand to `Self: Tr`
+ /// and `<Self as Tr>::A: B`.
+ SelfAndAssociatedTypeBounds,
+}
+
pub trait AstConv<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx>;
@@ -239,7 +249,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
var: ty::BoundVar::from_u32(index),
kind: ty::BrNamed(def_id, name),
};
- tcx.mk_re_late_bound(debruijn, br)
+ ty::Region::new_late_bound(tcx, debruijn, br)
}
Some(rbv::ResolvedArg::EarlyBound(def_id)) => {
@@ -247,12 +257,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
- tcx.mk_re_early_bound(ty::EarlyBoundRegion { def_id, index, name })
+ ty::Region::new_early_bound(tcx, ty::EarlyBoundRegion { def_id, index, name })
}
Some(rbv::ResolvedArg::Free(scope, id)) => {
let name = lifetime_name(id.expect_local());
- tcx.mk_re_free(scope, ty::BrNamed(id, name))
+ ty::Region::new_free(tcx, scope, ty::BrNamed(id, name))
// (*) -- not late-bound, won't change
}
@@ -269,7 +279,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// elision. `resolve_lifetime` should have
// reported an error in this case -- but if
// not, let's error out.
- tcx.mk_re_error_with_message(
+ ty::Region::new_error_with_message(
+ tcx,
lifetime.ident.span,
"unelided lifetime in signature",
)
@@ -432,7 +443,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) {
self.inferred_params.push(ty.span);
- tcx.ty_error_misc().into()
+ Ty::new_misc_error(tcx).into()
} else {
self.astconv.ast_ty_to_ty(ty).into()
}
@@ -463,7 +474,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.astconv.ct_infer(ty, Some(param), inf.span).into()
} else {
self.inferred_params.push(inf.span);
- tcx.const_error_misc(ty).into()
+ ty::Const::new_misc_error(tcx, ty).into()
}
}
_ => unreachable!(),
@@ -485,7 +496,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!(?param, "unelided lifetime in signature");
// This indicates an illegal lifetime in a non-assoc-trait position
- tcx.mk_re_error_with_message(
+ ty::Region::new_error_with_message(
+ tcx,
self.span,
"unelided lifetime in signature",
)
@@ -500,14 +512,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
_ => false,
}) {
// Avoid ICE #86756 when type error recovery goes awry.
- return tcx.ty_error_misc().into();
+ return Ty::new_misc_error(tcx).into();
}
tcx.at(self.span).type_of(param.def_id).subst(tcx, substs).into()
} else if infer_args {
self.astconv.ty_infer(Some(param), self.span).into()
} else {
// We've already errored above about the mismatch.
- tcx.ty_error_misc().into()
+ Ty::new_misc_error(tcx).into()
}
}
GenericParamDefKind::Const { has_default } => {
@@ -517,7 +529,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.no_bound_vars()
.expect("const parameter types cannot be generic");
if let Err(guar) = ty.error_reported() {
- return tcx.const_error(ty, guar).into();
+ return ty::Const::new_error(tcx, guar, ty).into();
}
if !infer_args && has_default {
tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
@@ -526,7 +538,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.astconv.ct_infer(ty, Some(param), self.span).into()
} else {
// We've already errored above about the mismatch.
- tcx.const_error_misc(ty).into()
+ ty::Const::new_misc_error(tcx, ty).into()
}
}
}
@@ -884,551 +896,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.is_some()
}
- /// Sets `implicitly_sized` to true on `Bounds` if necessary
- pub(crate) fn add_implicitly_sized(
- &self,
- bounds: &mut Bounds<'tcx>,
- self_ty: Ty<'tcx>,
- ast_bounds: &'tcx [hir::GenericBound<'tcx>],
- self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>,
- span: Span,
- ) {
- let tcx = self.tcx();
-
- // Try to find an unbound in bounds.
- let mut unbound = None;
- let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
- for ab in ast_bounds {
- if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
- if unbound.is_none() {
- unbound = Some(&ptr.trait_ref);
- } else {
- tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
- }
- }
- }
- };
- search_bounds(ast_bounds);
- if let Some((self_ty, where_clause)) = self_ty_where_predicates {
- for clause in where_clause {
- if let hir::WherePredicate::BoundPredicate(pred) = clause {
- if pred.is_param_bound(self_ty.to_def_id()) {
- search_bounds(pred.bounds);
- }
- }
- }
- }
-
- let sized_def_id = tcx.lang_items().sized_trait();
- match (&sized_def_id, unbound) {
- (Some(sized_def_id), Some(tpb))
- if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
- {
- // There was in fact a `?Sized` bound, return without doing anything
- return;
- }
- (_, Some(_)) => {
- // There was a `?Trait` bound, but it was not `?Sized`; warn.
- tcx.sess.span_warn(
- span,
- "default bound relaxed for a type parameter, but \
- this does nothing because the given bound is not \
- a default; only `?Sized` is supported",
- );
- // Otherwise, add implicitly sized if `Sized` is available.
- }
- _ => {
- // There was no `?Sized` bound; add implicitly sized if `Sized` is available.
- }
- }
- if sized_def_id.is_none() {
- // No lang item for `Sized`, so we can't add it as a bound.
- return;
- }
- bounds.push_sized(tcx, self_ty, span);
- }
-
- /// This helper takes a *converted* parameter type (`param_ty`)
- /// and an *unconverted* list of bounds:
- ///
- /// ```text
- /// fn foo<T: Debug>
- /// ^ ^^^^^ `ast_bounds` parameter, in HIR form
- /// |
- /// `param_ty`, in ty form
- /// ```
- ///
- /// It adds these `ast_bounds` into the `bounds` structure.
- ///
- /// **A note on binders:** there is an implied binder around
- /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
- /// for more details.
- #[instrument(level = "debug", skip(self, ast_bounds, bounds))]
- pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: I,
- bounds: &mut Bounds<'tcx>,
- bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
- only_self_bounds: OnlySelfBounds,
- ) {
- for ast_bound in ast_bounds {
- match ast_bound {
- hir::GenericBound::Trait(poly_trait_ref, modifier) => {
- let (constness, polarity) = match modifier {
- hir::TraitBoundModifier::MaybeConst => {
- (ty::BoundConstness::ConstIfConst, ty::ImplPolarity::Positive)
- }
- hir::TraitBoundModifier::None => {
- (ty::BoundConstness::NotConst, ty::ImplPolarity::Positive)
- }
- hir::TraitBoundModifier::Negative => {
- (ty::BoundConstness::NotConst, ty::ImplPolarity::Negative)
- }
- hir::TraitBoundModifier::Maybe => continue,
- };
- let _ = self.instantiate_poly_trait_ref(
- &poly_trait_ref.trait_ref,
- poly_trait_ref.span,
- constness,
- polarity,
- param_ty,
- bounds,
- false,
- only_self_bounds,
- );
- }
- &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
- self.instantiate_lang_item_trait_ref(
- lang_item,
- span,
- hir_id,
- args,
- param_ty,
- bounds,
- only_self_bounds,
- );
- }
- hir::GenericBound::Outlives(lifetime) => {
- let region = self.ast_region_to_region(lifetime, None);
- bounds.push_region_bound(
- self.tcx(),
- ty::Binder::bind_with_vars(
- ty::OutlivesPredicate(param_ty, region),
- bound_vars,
- ),
- lifetime.ident.span,
- );
- }
- }
- }
- }
-
- /// Translates a list of bounds from the HIR into the `Bounds` data structure.
- /// The self-type for the bounds is given by `param_ty`.
- ///
- /// Example:
- ///
- /// ```ignore (illustrative)
- /// fn foo<T: Bar + Baz>() { }
- /// // ^ ^^^^^^^^^ ast_bounds
- /// // param_ty
- /// ```
- ///
- /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
- /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
- /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
- ///
- /// `span` should be the declaration size of the parameter.
- pub(crate) fn compute_bounds(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: &[hir::GenericBound<'_>],
- only_self_bounds: OnlySelfBounds,
- ) -> Bounds<'tcx> {
- let mut bounds = Bounds::default();
- self.add_bounds(
- param_ty,
- ast_bounds.iter(),
- &mut bounds,
- ty::List::empty(),
- only_self_bounds,
- );
- debug!(?bounds);
-
- bounds
- }
-
- /// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
- /// named `assoc_name` into ty::Bounds. Ignore the rest.
- pub(crate) fn compute_bounds_that_match_assoc_item(
- &self,
- param_ty: Ty<'tcx>,
- ast_bounds: &[hir::GenericBound<'_>],
- assoc_name: Ident,
- ) -> Bounds<'tcx> {
- let mut result = Vec::new();
-
- for ast_bound in ast_bounds {
- if let Some(trait_ref) = ast_bound.trait_ref()
- && let Some(trait_did) = trait_ref.trait_def_id()
- && self.tcx().trait_may_define_assoc_item(trait_did, assoc_name)
- {
- result.push(ast_bound.clone());
- }
- }
-
- let mut bounds = Bounds::default();
- self.add_bounds(
- param_ty,
- result.iter(),
- &mut bounds,
- ty::List::empty(),
- OnlySelfBounds(true),
- );
- debug!(?bounds);
-
- bounds
- }
-
- /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
- /// onto `bounds`.
- ///
- /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
- /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
- /// the binder (e.g., `&'a u32`) and hence may reference bound regions.
- #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
- fn add_predicates_for_ast_type_binding(
- &self,
- hir_ref_id: hir::HirId,
- trait_ref: ty::PolyTraitRef<'tcx>,
- binding: &ConvertedBinding<'_, 'tcx>,
- bounds: &mut Bounds<'tcx>,
- speculative: bool,
- dup_bindings: &mut FxHashMap<DefId, Span>,
- path_span: Span,
- constness: ty::BoundConstness,
- only_self_bounds: OnlySelfBounds,
- polarity: ty::ImplPolarity,
- ) -> Result<(), ErrorGuaranteed> {
- // Given something like `U: SomeTrait<T = X>`, we want to produce a
- // predicate like `<U as SomeTrait>::T = X`. This is somewhat
- // subtle in the event that `T` is defined in a supertrait of
- // `SomeTrait`, because in that case we need to upcast.
- //
- // That is, consider this case:
- //
- // ```
- // trait SubTrait: SuperTrait<i32> { }
- // trait SuperTrait<A> { type T; }
- //
- // ... B: SubTrait<T = foo> ...
- // ```
- //
- // We want to produce `<B as SuperTrait<i32>>::T == foo`.
-
- let tcx = self.tcx();
-
- let return_type_notation =
- binding.gen_args.parenthesized == hir::GenericArgsParentheses::ReturnTypeNotation;
-
- let candidate = if return_type_notation {
- if self.trait_defines_associated_item_named(
- trait_ref.def_id(),
- ty::AssocKind::Fn,
- binding.item_name,
- ) {
- trait_ref
- } else {
- self.one_bound_for_assoc_method(
- traits::supertraits(tcx, trait_ref),
- trait_ref.print_only_trait_path(),
- binding.item_name,
- path_span,
- )?
- }
- } else if self.trait_defines_associated_item_named(
- trait_ref.def_id(),
- ty::AssocKind::Type,
- binding.item_name,
- ) {
- // Simple case: X is defined in the current trait.
- trait_ref
- } else {
- // Otherwise, we have to walk through the supertraits to find
- // those that do.
- self.one_bound_for_assoc_type(
- || traits::supertraits(tcx, trait_ref),
- trait_ref.skip_binder().print_only_trait_name(),
- binding.item_name,
- path_span,
- match binding.kind {
- ConvertedBindingKind::Equality(term) => Some(term),
- _ => None,
- },
- )?
- };
-
- let (assoc_ident, def_scope) =
- tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
-
- // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
- // of calling `filter_by_name_and_kind`.
- let find_item_of_kind = |kind| {
- tcx.associated_items(candidate.def_id())
- .filter_by_name_unhygienic(assoc_ident.name)
- .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
- };
- let assoc_item = if return_type_notation {
- find_item_of_kind(ty::AssocKind::Fn)
- } else {
- find_item_of_kind(ty::AssocKind::Type)
- .or_else(|| find_item_of_kind(ty::AssocKind::Const))
- }
- .expect("missing associated type");
-
- if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
- tcx.sess
- .struct_span_err(
- binding.span,
- format!("{} `{}` is private", assoc_item.kind, binding.item_name),
- )
- .span_label(binding.span, format!("private {}", assoc_item.kind))
- .emit();
- }
- tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
-
- if !speculative {
- dup_bindings
- .entry(assoc_item.def_id)
- .and_modify(|prev_span| {
- tcx.sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
- span: binding.span,
- prev_span: *prev_span,
- item_name: binding.item_name,
- def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
- });
- })
- .or_insert(binding.span);
- }
-
- let projection_ty = if return_type_notation {
- // If we have an method return type bound, then we need to substitute
- // the method's early bound params with suitable late-bound params.
- let mut num_bound_vars = candidate.bound_vars().len();
- let substs =
- candidate.skip_binder().substs.extend_to(tcx, assoc_item.def_id, |param, _| {
- let subst = match param.kind {
- GenericParamDefKind::Lifetime => tcx
- .mk_re_late_bound(
- ty::INNERMOST,
- ty::BoundRegion {
- var: ty::BoundVar::from_usize(num_bound_vars),
- kind: ty::BoundRegionKind::BrNamed(param.def_id, param.name),
- },
- )
- .into(),
- GenericParamDefKind::Type { .. } => tcx
- .mk_bound(
- ty::INNERMOST,
- ty::BoundTy {
- var: ty::BoundVar::from_usize(num_bound_vars),
- kind: ty::BoundTyKind::Param(param.def_id, param.name),
- },
- )
- .into(),
- GenericParamDefKind::Const { .. } => {
- let ty = tcx
- .type_of(param.def_id)
- .no_bound_vars()
- .expect("ct params cannot have early bound vars");
- tcx.mk_const(
- ty::ConstKind::Bound(
- ty::INNERMOST,
- ty::BoundVar::from_usize(num_bound_vars),
- ),
- ty,
- )
- .into()
- }
- };
- num_bound_vars += 1;
- subst
- });
-
- // Next, we need to check that the return-type notation is being used on
- // an RPITIT (return-position impl trait in trait) or AFIT (async fn in trait).
- let output = tcx.fn_sig(assoc_item.def_id).skip_binder().output();
- let output = if let ty::Alias(ty::Projection, alias_ty) = *output.skip_binder().kind()
- && tcx.def_kind(alias_ty.def_id) == DefKind::ImplTraitPlaceholder
- {
- alias_ty
- } else {
- return Err(self.tcx().sess.emit_err(
- crate::errors::ReturnTypeNotationOnNonRpitit {
- span: binding.span,
- ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
- fn_span: tcx.hir().span_if_local(assoc_item.def_id),
- note: (),
- },
- ));
- };
-
- // Finally, move the fn return type's bound vars over to account for the early bound
- // params (and trait ref's late bound params). This logic is very similar to
- // `Predicate::subst_supertrait`, and it's no coincidence why.
- let shifted_output = tcx.shift_bound_var_indices(num_bound_vars, output);
- let subst_output = ty::EarlyBinder(shifted_output).subst(tcx, substs);
-
- let bound_vars = tcx.late_bound_vars(binding.hir_id);
- ty::Binder::bind_with_vars(subst_output, bound_vars)
- } else {
- // Include substitutions for generic parameters of associated types
- candidate.map_bound(|trait_ref| {
- let ident = Ident::new(assoc_item.name, binding.item_name.span);
- let item_segment = hir::PathSegment {
- ident,
- hir_id: binding.hir_id,
- res: Res::Err,
- args: Some(binding.gen_args),
- infer_args: false,
- };
-
- let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
- path_span,
- assoc_item.def_id,
- &item_segment,
- trait_ref.substs,
- );
-
- debug!(?substs_trait_ref_and_assoc_item);
-
- tcx.mk_alias_ty(assoc_item.def_id, substs_trait_ref_and_assoc_item)
- })
- };
-
- if !speculative {
- // Find any late-bound regions declared in `ty` that are not
- // declared in the trait-ref or assoc_item. These are not well-formed.
- //
- // Example:
- //
- // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
- // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
- if let ConvertedBindingKind::Equality(ty) = binding.kind {
- let late_bound_in_trait_ref =
- tcx.collect_constrained_late_bound_regions(&projection_ty);
- let late_bound_in_ty =
- tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
- debug!(?late_bound_in_trait_ref);
- debug!(?late_bound_in_ty);
-
- // FIXME: point at the type params that don't have appropriate lifetimes:
- // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
- // ---- ---- ^^^^^^^
- self.validate_late_bound_regions(
- late_bound_in_trait_ref,
- late_bound_in_ty,
- |br_name| {
- struct_span_err!(
- tcx.sess,
- binding.span,
- E0582,
- "binding for associated type `{}` references {}, \
- which does not appear in the trait input types",
- binding.item_name,
- br_name
- )
- },
- );
- }
- }
-
- match binding.kind {
- ConvertedBindingKind::Equality(..) if return_type_notation => {
- return Err(self.tcx().sess.emit_err(
- crate::errors::ReturnTypeNotationEqualityBound { span: binding.span },
- ));
- }
- ConvertedBindingKind::Equality(mut term) => {
- // "Desugar" a constraint like `T: Iterator<Item = u32>` this to
- // the "projection predicate" for:
- //
- // `<T as Iterator>::Item = u32`
- let assoc_item_def_id = projection_ty.skip_binder().def_id;
- let def_kind = tcx.def_kind(assoc_item_def_id);
- match (def_kind, term.unpack()) {
- (hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
- | (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
- (_, _) => {
- let got = if let Some(_) = term.ty() { "type" } else { "constant" };
- let expected = tcx.def_descr(assoc_item_def_id);
- let mut err = tcx.sess.struct_span_err(
- binding.span,
- format!("expected {expected} bound, found {got}"),
- );
- err.span_note(
- tcx.def_span(assoc_item_def_id),
- format!("{expected} defined here"),
- );
-
- if let hir::def::DefKind::AssocConst = def_kind
- && let Some(t) = term.ty() && (t.is_enum() || t.references_error())
- && tcx.features().associated_const_equality {
- err.span_suggestion(
- binding.span,
- "if equating a const, try wrapping with braces",
- format!("{} = {{ const }}", binding.item_name),
- Applicability::HasPlaceholders,
- );
- }
- let reported = err.emit();
- term = match def_kind {
- hir::def::DefKind::AssocTy => tcx.ty_error(reported).into(),
- hir::def::DefKind::AssocConst => tcx
- .const_error(
- tcx.type_of(assoc_item_def_id)
- .subst(tcx, projection_ty.skip_binder().substs),
- reported,
- )
- .into(),
- _ => unreachable!(),
- };
- }
- }
- bounds.push_projection_bound(
- tcx,
- projection_ty
- .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }),
- binding.span,
- );
- }
- ConvertedBindingKind::Constraint(ast_bounds) => {
- // "Desugar" a constraint like `T: Iterator<Item: Debug>` to
- //
- // `<T as Iterator>::Item: Debug`
- //
- // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
- // parameter to have a skipped binder.
- //
- // NOTE: If `only_self_bounds` is true, do NOT expand this associated
- // type bound into a trait predicate, since we only want to add predicates
- // for the `Self` type.
- if !only_self_bounds.0 {
- let param_ty = tcx.mk_alias(ty::Projection, projection_ty.skip_binder());
- self.add_bounds(
- param_ty,
- ast_bounds.iter(),
- bounds,
- projection_ty.bound_vars(),
- only_self_bounds,
- );
- }
- }
- }
- Ok(())
- }
-
fn ast_path_to_ty(
&self,
span: Span,
@@ -1436,384 +903,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_segment: &hir::PathSegment<'_>,
) -> Ty<'tcx> {
let substs = self.ast_path_substs_for_ty(span, did, item_segment);
- self.tcx().at(span).type_of(did).subst(self.tcx(), substs)
- }
-
- fn conv_object_ty_poly_trait_ref(
- &self,
- span: Span,
- hir_trait_bounds: &[hir::PolyTraitRef<'_>],
- lifetime: &hir::Lifetime,
- borrowed: bool,
- representation: DynKind,
- ) -> Ty<'tcx> {
- let tcx = self.tcx();
-
- let mut bounds = Bounds::default();
- let mut potential_assoc_types = Vec::new();
- let dummy_self = self.tcx().types.trait_object_dummy_self;
- for trait_bound in hir_trait_bounds.iter().rev() {
- if let GenericArgCountResult {
- correct:
- Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
- ..
- } = self.instantiate_poly_trait_ref(
- &trait_bound.trait_ref,
- trait_bound.span,
- ty::BoundConstness::NotConst,
- ty::ImplPolarity::Positive,
- dummy_self,
- &mut bounds,
- false,
- // FIXME: This should be `true`, but we don't really handle
- // associated type bounds or type aliases in objects in a way
- // that makes this meaningful, I think.
- OnlySelfBounds(false),
- ) {
- potential_assoc_types.extend(cur_potential_assoc_types);
- }
- }
-
- let mut trait_bounds = vec![];
- let mut projection_bounds = vec![];
- for (pred, span) in bounds.predicates() {
- let bound_pred = pred.kind();
- match bound_pred.skip_binder() {
- ty::PredicateKind::Clause(clause) => match clause {
- ty::Clause::Trait(trait_pred) => {
- assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
- trait_bounds.push((
- bound_pred.rebind(trait_pred.trait_ref),
- span,
- trait_pred.constness,
- ));
- }
- ty::Clause::Projection(proj) => {
- projection_bounds.push((bound_pred.rebind(proj), span));
- }
- ty::Clause::TypeOutlives(_) => {
- // Do nothing, we deal with regions separately
- }
- ty::Clause::RegionOutlives(_) | ty::Clause::ConstArgHasType(..) => bug!(),
- },
- ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::Subtype(_)
- | ty::PredicateKind::Coerce(_)
- | ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::ConstEquate(_, _)
- | ty::PredicateKind::TypeWellFormedFromEnv(_)
- | ty::PredicateKind::Ambiguous => bug!(),
- }
- }
-
- // Expand trait aliases recursively and check that only one regular (non-auto) trait
- // is used and no 'maybe' bounds are used.
- let expanded_traits =
- traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
-
- let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
- .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
- .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
- if regular_traits.len() > 1 {
- let first_trait = &regular_traits[0];
- let additional_trait = &regular_traits[1];
- let mut err = struct_span_err!(
- tcx.sess,
- additional_trait.bottom().1,
- E0225,
- "only auto traits can be used as additional traits in a trait object"
- );
- additional_trait.label_with_exp_info(
- &mut err,
- "additional non-auto trait",
- "additional use",
- );
- first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
- err.help(format!(
- "consider creating a new trait with all of these as supertraits and using that \
- trait here instead: `trait NewTrait: {} {{}}`",
- regular_traits
- .iter()
- .map(|t| t.trait_ref().print_only_trait_path().to_string())
- .collect::<Vec<_>>()
- .join(" + "),
- ));
- err.note(
- "auto-traits like `Send` and `Sync` are traits that have special properties; \
- for more information on them, visit \
- <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
- );
- err.emit();
- }
-
- if regular_traits.is_empty() && auto_traits.is_empty() {
- let trait_alias_span = trait_bounds
- .iter()
- .map(|&(trait_ref, _, _)| trait_ref.def_id())
- .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
- .map(|trait_ref| tcx.def_span(trait_ref));
- let reported =
- tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
- return tcx.ty_error(reported);
- }
-
- // Check that there are no gross object safety violations;
- // most importantly, that the supertraits don't contain `Self`,
- // to avoid ICEs.
- for item in &regular_traits {
- let object_safety_violations =
- astconv_object_safety_violations(tcx, item.trait_ref().def_id());
- if !object_safety_violations.is_empty() {
- let reported = report_object_safety_error(
- tcx,
- span,
- item.trait_ref().def_id(),
- &object_safety_violations,
- )
- .emit();
- return tcx.ty_error(reported);
- }
- }
-
- // Use a `BTreeSet` to keep output in a more consistent order.
- let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
-
- let regular_traits_refs_spans = trait_bounds
- .into_iter()
- .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
-
- for (base_trait_ref, span, constness) in regular_traits_refs_spans {
- assert_eq!(constness, ty::BoundConstness::NotConst);
- let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
- for pred in traits::elaborate(tcx, [base_pred]) {
- debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
-
- let bound_predicate = pred.kind();
- match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
- let pred = bound_predicate.rebind(pred);
- associated_types.entry(span).or_default().extend(
- tcx.associated_items(pred.def_id())
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Type)
- .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none())
- .map(|item| item.def_id),
- );
- }
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
- let pred = bound_predicate.rebind(pred);
- // A `Self` within the original bound will be substituted with a
- // `trait_object_dummy_self`, so check for that.
- let references_self = match pred.skip_binder().term.unpack() {
- ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
- ty::TermKind::Const(c) => {
- c.ty().walk().any(|arg| arg == dummy_self.into())
- }
- };
-
- // If the projection output contains `Self`, force the user to
- // elaborate it explicitly to avoid a lot of complexity.
- //
- // The "classically useful" case is the following:
- // ```
- // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
- // type MyOutput;
- // }
- // ```
- //
- // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
- // but actually supporting that would "expand" to an infinitely-long type
- // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
- //
- // Instead, we force the user to write
- // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
- // the discussion in #56288 for alternatives.
- if !references_self {
- // Include projections defined on supertraits.
- projection_bounds.push((pred, span));
- }
- }
- _ => (),
- }
- }
- }
-
- for (projection_bound, _) in &projection_bounds {
- for def_ids in associated_types.values_mut() {
- def_ids.remove(&projection_bound.projection_def_id());
- }
- }
-
- self.complain_about_missing_associated_types(
- associated_types,
- potential_assoc_types,
- hir_trait_bounds,
- );
-
- // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
- // `dyn Trait + Send`.
- // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
- // the bounds
- let mut duplicates = FxHashSet::default();
- auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
- debug!("regular_traits: {:?}", regular_traits);
- debug!("auto_traits: {:?}", auto_traits);
-
- // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
- let existential_trait_refs = regular_traits.iter().map(|i| {
- i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
- assert_eq!(trait_ref.self_ty(), dummy_self);
-
- // Verify that `dummy_self` did not leak inside default type parameters. This
- // could not be done at path creation, since we need to see through trait aliases.
- let mut missing_type_params = vec![];
- let mut references_self = false;
- let generics = tcx.generics_of(trait_ref.def_id);
- let substs: Vec<_> = trait_ref
- .substs
- .iter()
- .enumerate()
- .skip(1) // Remove `Self` for `ExistentialPredicate`.
- .map(|(index, arg)| {
- if arg == dummy_self.into() {
- let param = &generics.params[index];
- missing_type_params.push(param.name);
- return tcx.ty_error_misc().into();
- } else if arg.walk().any(|arg| arg == dummy_self.into()) {
- references_self = true;
- return tcx.ty_error_misc().into();
- }
- arg
- })
- .collect();
- let substs = tcx.mk_substs(&substs);
-
- let span = i.bottom().1;
- let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
- hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
- && hir_bound.span.contains(span)
- });
- self.complain_about_missing_type_params(
- missing_type_params,
- trait_ref.def_id,
- span,
- empty_generic_args,
- );
+ let ty = self.tcx().at(span).type_of(did);
- if references_self {
- let def_id = i.bottom().0.def_id();
- let mut err = struct_span_err!(
- tcx.sess,
- i.bottom().1,
- E0038,
- "the {} `{}` cannot be made into an object",
- tcx.def_descr(def_id),
- tcx.item_name(def_id),
- );
- err.note(
- rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
- .error_msg(),
- );
- err.emit();
- }
-
- ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
- })
- });
-
- let existential_projections = projection_bounds
- .iter()
- // We filter out traits that don't have `Self` as their self type above,
- // we need to do the same for projections.
- .filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
- .map(|(bound, _)| {
- bound.map_bound(|mut b| {
- assert_eq!(b.projection_ty.self_ty(), dummy_self);
-
- // Like for trait refs, verify that `dummy_self` did not leak inside default type
- // parameters.
- let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
- if arg.walk().any(|arg| arg == dummy_self.into()) {
- return true;
- }
- false
- });
- if references_self {
- let guar = tcx.sess.delay_span_bug(
- span,
- "trait object projection bounds reference `Self`",
- );
- let substs: Vec<_> = b
- .projection_ty
- .substs
- .iter()
- .map(|arg| {
- if arg.walk().any(|arg| arg == dummy_self.into()) {
- return tcx.ty_error(guar).into();
- }
- arg
- })
- .collect();
- b.projection_ty.substs = tcx.mk_substs(&substs);
- }
-
- ty::ExistentialProjection::erase_self_ty(tcx, b)
- })
- });
-
- let regular_trait_predicates = existential_trait_refs
- .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
- let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
- ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
- });
- // N.b. principal, projections, auto traits
- // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
- let mut v = regular_trait_predicates
- .chain(
- existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
- )
- .chain(auto_trait_predicates)
- .collect::<SmallVec<[_; 8]>>();
- v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
- v.dedup();
- let existential_predicates = tcx.mk_poly_existential_predicates(&v);
-
- // Use explicitly-specified region bound.
- let region_bound = if !lifetime.is_elided() {
- self.ast_region_to_region(lifetime, None)
+ if matches!(self.tcx().def_kind(did), DefKind::TyAlias)
+ && (ty.skip_binder().has_opaque_types() || self.tcx().features().lazy_type_alias)
+ {
+ // Type aliases referring to types that contain opaque types (but aren't just directly
+ // referencing a single opaque type) get encoded as a type alias that normalization will
+ // then actually instantiate the where bounds of.
+ let alias_ty = self.tcx().mk_alias_ty(did, substs);
+ Ty::new_alias(self.tcx(), ty::Weak, alias_ty)
} else {
- self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
- if tcx.named_bound_var(lifetime.hir_id).is_some() {
- self.ast_region_to_region(lifetime, None)
- } else {
- self.re_infer(None, span).unwrap_or_else(|| {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0228,
- "the lifetime bound for this object type cannot be deduced \
- from context; please supply an explicit bound"
- );
- let e = if borrowed {
- // We will have already emitted an error E0106 complaining about a
- // missing named lifetime in `&dyn Trait`, so we elide this one.
- err.delay_as_bug()
- } else {
- err.emit()
- };
- tcx.mk_re_error(e)
- })
- }
- })
- };
- debug!("region_bound: {:?}", region_bound);
-
- let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation);
- debug!("trait_object_type: {:?}", ty);
- ty
+ ty.subst(self.tcx(), substs)
+ }
}
fn report_ambiguous_associated_type(
@@ -1948,9 +1050,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|| {
traits::transitive_bounds_that_define_assoc_item(
tcx,
- predicates.iter().filter_map(|(p, _)| {
- Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref))
- }),
+ predicates
+ .iter()
+ .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
assoc_name,
)
},
@@ -2408,6 +1510,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
let tcx = self.tcx();
+ // Don't attempt to look up inherent associated types when the feature is not enabled.
+ // Theoretically it'd be fine to do so since we feature-gate their definition site.
+ // However, due to current limitations of the implementation (caused by us performing
+ // selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the
+ // feature-gate error, needlessly confusing users that use IATs by accident (#113265).
+ if !tcx.features().inherent_associated_types {
+ return Ok(None);
+ }
+
let candidates: Vec<_> = tcx
.inherent_impls(adt_did)
.iter()
@@ -2441,32 +1552,64 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let mut fulfillment_errors = Vec::new();
let mut applicable_candidates: Vec<_> = infcx.probe(|_| {
- let universe = infcx.create_next_universe();
-
// Regions are not considered during selection.
- // FIXME(non_lifetime_binders): Here we are "truncating" or "flattening" the universes
- // of type and const binders. Is that correct in the selection phase? See also #109505.
- let self_ty = tcx.replace_escaping_bound_vars_uncached(
- self_ty,
- FnMutDelegate {
- regions: &mut |_| tcx.lifetimes.re_erased,
- types: &mut |bv| {
- tcx.mk_placeholder(ty::PlaceholderType { universe, bound: bv })
- },
- consts: &mut |bv, ty| {
- tcx.mk_const(ty::PlaceholderConst { universe, bound: bv }, ty)
- },
- },
- );
+ let self_ty = self_ty
+ .fold_with(&mut BoundVarEraser { tcx, universe: infcx.create_next_universe() });
+
+ struct BoundVarEraser<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ universe: ty::UniverseIndex,
+ }
+
+ // FIXME(non_lifetime_binders): Don't assign the same universe to each placeholder.
+ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarEraser<'tcx> {
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r }
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ match *ty.kind() {
+ ty::Bound(_, bv) => Ty::new_placeholder(
+ self.tcx,
+ ty::PlaceholderType { universe: self.universe, bound: bv },
+ ),
+ _ => ty.super_fold_with(self),
+ }
+ }
+
+ fn fold_const(
+ &mut self,
+ ct: ty::Const<'tcx>,
+ ) -> <TyCtxt<'tcx> as rustc_type_ir::Interner>::Const {
+ assert!(!ct.ty().has_escaping_bound_vars());
+
+ match ct.kind() {
+ ty::ConstKind::Bound(_, bv) => ty::Const::new_placeholder(
+ self.tcx,
+ ty::PlaceholderConst { universe: self.universe, bound: bv },
+ ct.ty(),
+ ),
+ _ => ct.super_fold_with(self),
+ }
+ }
+ }
+
+ let InferOk { value: self_ty, obligations } =
+ infcx.at(&cause, param_env).normalize(self_ty);
candidates
.iter()
.copied()
.filter(|&(impl_, _)| {
infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(&infcx);
+ let ocx = ObligationCtxt::new(&infcx);
+ ocx.register_obligations(obligations.clone());
- let impl_substs = infcx.fresh_item_substs(impl_);
+ let impl_substs = infcx.fresh_substs_for_item(span, impl_);
let impl_ty = tcx.type_of(impl_).subst(tcx, impl_substs);
let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
@@ -2522,7 +1665,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.chain(substs.into_iter().skip(parent_substs.len())),
);
- let ty = tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
+ let ty = Ty::new_alias(tcx, ty::Inherent, tcx.mk_alias_ty(assoc_item, substs));
return Ok(Some((ty, assoc_item)));
}
@@ -2707,7 +1850,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&[path_str],
item_segment.ident.name,
);
- return tcx.ty_error(reported)
+ return Ty::new_error(tcx,reported)
};
debug!("qpath_to_ty: self_type={:?}", self_ty);
@@ -2730,7 +1873,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
- tcx.mk_projection(item_def_id, item_substs)
+ Ty::new_projection(tcx, item_def_id, item_substs)
}
pub fn prohibit_generics<'a>(
@@ -2993,7 +2136,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
err.note("`impl Trait` types can't have type parameters");
});
let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
- tcx.mk_opaque(did, substs)
+ Ty::new_opaque(tcx, did, substs)
}
Res::Def(
DefKind::Enum
@@ -3045,16 +2188,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
var: ty::BoundVar::from_u32(index),
kind: ty::BoundTyKind::Param(def_id, name),
};
- tcx.mk_bound(debruijn, br)
+ Ty::new_bound(tcx, debruijn, br)
}
Some(rbv::ResolvedArg::EarlyBound(_)) => {
let def_id = def_id.expect_local();
let item_def_id = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
- tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
+ Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id))
}
- Some(rbv::ResolvedArg::Error(guar)) => tcx.ty_error(guar),
+ Some(rbv::ResolvedArg::Error(guar)) => Ty::new_error(tcx, guar),
arg => bug!("unexpected bound var resolution for {hir_id:?}: {arg:?}"),
}
}
@@ -3166,7 +2309,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
{
err.span_note(impl_.self_ty.span, "not a concrete type");
}
- tcx.ty_error(err.emit())
+ Ty::new_error(tcx, err.emit())
} else {
ty
}
@@ -3207,9 +2350,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
match prim_ty {
hir::PrimTy::Bool => tcx.types.bool,
hir::PrimTy::Char => tcx.types.char,
- hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)),
- hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)),
- hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)),
+ hir::PrimTy::Int(it) => Ty::new_int(tcx, ty::int_ty(it)),
+ hir::PrimTy::Uint(uit) => Ty::new_uint(tcx, ty::uint_ty(uit)),
+ hir::PrimTy::Float(ft) => Ty::new_float(tcx, ty::float_ty(ft)),
hir::PrimTy::Str => tcx.types.str_,
}
}
@@ -3219,7 +2362,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
.sess
.delay_span_bug(path.span, "path with `Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
- self.tcx().ty_error(e)
+ Ty::new_error(self.tcx(), e)
}
_ => span_bug!(span, "unexpected resolution: {:?}", path.res),
}
@@ -3244,31 +2387,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let tcx = self.tcx();
let result_ty = match &ast_ty.kind {
- hir::TyKind::Slice(ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
+ hir::TyKind::Slice(ty) => Ty::new_slice(tcx, self.ast_ty_to_ty(ty)),
hir::TyKind::Ptr(mt) => {
- tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
}
hir::TyKind::Ref(region, mt) => {
let r = self.ast_region_to_region(region, None);
debug!(?r);
let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
- tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
+ Ty::new_ref(tcx, r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
}
hir::TyKind::Never => tcx.types.never,
hir::TyKind::Tup(fields) => {
- tcx.mk_tup_from_iter(fields.iter().map(|t| self.ast_ty_to_ty(t)))
+ Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t)))
}
hir::TyKind::BareFn(bf) => {
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
- tcx.mk_fn_ptr(self.ty_of_fn(
- ast_ty.hir_id,
- bf.unsafety,
- bf.abi,
- bf.decl,
- None,
- Some(ast_ty),
- ))
+ Ty::new_fn_ptr(
+ tcx,
+ self.ty_of_fn(ast_ty.hir_id, bf.unsafety, bf.abi, bf.decl, None, Some(ast_ty)),
+ )
}
hir::TyKind::TraitObject(bounds, lifetime, repr) => {
self.maybe_lint_bare_trait(ast_ty, in_path);
@@ -3277,7 +2416,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
TraitObjectSyntax::DynStar => ty::DynStar,
};
- self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
+ self.conv_object_ty_poly_trait_ref(
+ ast_ty.span,
+ ast_ty.hir_id,
+ bounds,
+ lifetime,
+ borrowed,
+ repr,
+ )
}
hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
@@ -3288,7 +2434,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let opaque_ty = tcx.hir().item(item_id);
match opaque_ty.kind {
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+ hir::ItemKind::OpaqueTy(&hir::OpaqueTy { origin, .. }) => {
let local_def_id = item_id.owner_id.def_id;
// If this is an RPITIT and we are using the new RPITIT lowering scheme, we
// generate the def_id of an associated type for the trait and return as
@@ -3308,7 +2454,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let ty = self.ast_ty_to_ty_inner(qself, false, true);
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
.map(|(ty, _, _)| ty)
- .unwrap_or_else(|guar| tcx.ty_error(guar))
+ .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
}
&hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
let def_id = tcx.require_lang_item(lang_item, Some(span));
@@ -3332,7 +2478,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
};
- tcx.mk_array_with_const_len(self.ast_ty_to_ty(ty), length)
+ Ty::new_array_with_const_len(tcx, self.ast_ty_to_ty(ty), length)
}
hir::TyKind::Typeof(e) => {
let ty_erased = tcx.type_of(e.def_id).subst_identity();
@@ -3356,7 +2502,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// handled specially and will not descend into this routine.
self.ty_infer(None, ast_ty.span)
}
- hir::TyKind::Err(guar) => tcx.ty_error(*guar),
+ hir::TyKind::Err(guar) => Ty::new_error(tcx, *guar),
};
self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
@@ -3393,7 +2539,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
});
debug!("impl_trait_ty_to_ty: substs={:?}", substs);
- if in_trait { tcx.mk_projection(def_id, substs) } else { tcx.mk_opaque(def_id, substs) }
+ if in_trait {
+ Ty::new_projection(tcx, def_id, substs)
+ } else {
+ Ty::new_opaque(tcx, def_id, substs)
+ }
}
pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
@@ -3462,7 +2612,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
self.ast_ty_to_ty(output)
}
}
- hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
+ hir::FnRetTy::DefaultReturn(..) => Ty::new_unit(tcx,),
};
debug!(?output_ty);
@@ -3641,148 +2791,4 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
Some(r)
}
-
- /// Make sure that we are in the condition to suggest the blanket implementation.
- fn maybe_lint_blanket_trait_impl(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) {
- let tcx = self.tcx();
- let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
- if let hir::Node::Item(hir::Item {
- kind:
- hir::ItemKind::Impl(hir::Impl {
- self_ty: impl_self_ty, of_trait: Some(of_trait_ref), generics, ..
- }),
- ..
- }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id
- {
- if !of_trait_ref.trait_def_id().is_some_and(|def_id| def_id.is_local()) {
- return;
- }
- let of_trait_span = of_trait_ref.path.span;
- // make sure that we are not calling unwrap to abort during the compilation
- let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return; };
- let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; };
- // check if the trait has generics, to make a correct suggestion
- let param_name = generics.params.next_type_param_name(None);
-
- let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
- (span, format!(", {}: {}", param_name, impl_trait_name))
- } else {
- (generics.span, format!("<{}: {}>", param_name, impl_trait_name))
- };
- diag.multipart_suggestion(
- format!("alternatively use a blanket \
- implementation to implement `{of_trait_name}` for \
- all types that also implement `{impl_trait_name}`"),
- vec![
- (self_ty.span, param_name),
- add_generic_sugg,
- ],
- Applicability::MaybeIncorrect,
- );
- }
- }
-
- fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
- let tcx = self.tcx();
- if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
- self_ty.kind
- {
- let needs_bracket = in_path
- && !tcx
- .sess
- .source_map()
- .span_to_prev_source(self_ty.span)
- .ok()
- .is_some_and(|s| s.trim_end().ends_with('<'));
-
- let is_global = poly_trait_ref.trait_ref.path.is_global();
-
- let mut sugg = Vec::from_iter([(
- self_ty.span.shrink_to_lo(),
- format!(
- "{}dyn {}",
- if needs_bracket { "<" } else { "" },
- if is_global { "(" } else { "" },
- ),
- )]);
-
- if is_global || needs_bracket {
- sugg.push((
- self_ty.span.shrink_to_hi(),
- format!(
- "{}{}",
- if is_global { ")" } else { "" },
- if needs_bracket { ">" } else { "" },
- ),
- ));
- }
-
- if self_ty.span.edition().rust_2021() {
- let msg = "trait objects must include the `dyn` keyword";
- let label = "add `dyn` keyword before this trait";
- let mut diag =
- rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
- if self_ty.span.can_be_used_for_suggestions() {
- diag.multipart_suggestion_verbose(
- label,
- sugg,
- Applicability::MachineApplicable,
- );
- }
- // check if the impl trait that we are considering is a impl of a local trait
- self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
- diag.stash(self_ty.span, StashKey::TraitMissingMethod);
- } else {
- let msg = "trait objects without an explicit `dyn` are deprecated";
- tcx.struct_span_lint_hir(
- BARE_TRAIT_OBJECTS,
- self_ty.hir_id,
- self_ty.span,
- msg,
- |lint| {
- lint.multipart_suggestion_verbose(
- "use `dyn`",
- sugg,
- Applicability::MachineApplicable,
- );
- self.maybe_lint_blanket_trait_impl(&self_ty, lint);
- lint
- },
- );
- }
- }
- }
-}
-
-pub trait InferCtxtExt<'tcx> {
- fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx>;
-}
-
-impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
- fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> {
- InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind {
- GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(),
- GenericParamDefKind::Type { .. } => self
- .next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::SubstitutionPlaceholder,
- span: self.tcx.def_span(def_id),
- })
- .into(),
- GenericParamDefKind::Const { .. } => {
- let span = self.tcx.def_span(def_id);
- let origin = ConstVariableOrigin {
- kind: ConstVariableOriginKind::SubstitutionPlaceholder,
- span,
- };
- self.next_const_var(
- self.tcx
- .type_of(param.def_id)
- .no_bound_vars()
- .expect("const parameter types cannot be generic"),
- origin,
- )
- .into()
- }
- })
- }
}
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
new file mode 100644
index 000000000..9227ee934
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -0,0 +1,408 @@
+use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBounds};
+use crate::bounds::Bounds;
+use crate::errors::TraitObjectDeclaredWithNoTraits;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
+use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{DynKind, ToPredicate};
+use rustc_span::Span;
+use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
+use rustc_trait_selection::traits::{self, astconv_object_safety_violations};
+
+use smallvec::{smallvec, SmallVec};
+use std::collections::BTreeSet;
+
+use super::AstConv;
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+ pub(super) fn conv_object_ty_poly_trait_ref(
+ &self,
+ span: Span,
+ hir_id: hir::HirId,
+ hir_trait_bounds: &[hir::PolyTraitRef<'_>],
+ lifetime: &hir::Lifetime,
+ borrowed: bool,
+ representation: DynKind,
+ ) -> Ty<'tcx> {
+ let tcx = self.tcx();
+
+ let mut bounds = Bounds::default();
+ let mut potential_assoc_types = Vec::new();
+ let dummy_self = self.tcx().types.trait_object_dummy_self;
+ for trait_bound in hir_trait_bounds.iter().rev() {
+ if let GenericArgCountResult {
+ correct:
+ Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
+ ..
+ } = self.instantiate_poly_trait_ref(
+ &trait_bound.trait_ref,
+ trait_bound.span,
+ ty::BoundConstness::NotConst,
+ ty::ImplPolarity::Positive,
+ dummy_self,
+ &mut bounds,
+ false,
+ // FIXME: This should be `true`, but we don't really handle
+ // associated type bounds or type aliases in objects in a way
+ // that makes this meaningful, I think.
+ OnlySelfBounds(false),
+ ) {
+ potential_assoc_types.extend(cur_potential_assoc_types);
+ }
+ }
+
+ let mut trait_bounds = vec![];
+ let mut projection_bounds = vec![];
+ for (pred, span) in bounds.clauses() {
+ let bound_pred = pred.kind();
+ match bound_pred.skip_binder() {
+ ty::ClauseKind::Trait(trait_pred) => {
+ assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive);
+ trait_bounds.push((
+ bound_pred.rebind(trait_pred.trait_ref),
+ span,
+ trait_pred.constness,
+ ));
+ }
+ ty::ClauseKind::Projection(proj) => {
+ projection_bounds.push((bound_pred.rebind(proj), span));
+ }
+ ty::ClauseKind::TypeOutlives(_) => {
+ // Do nothing, we deal with regions separately
+ }
+ ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::ConstArgHasType(..)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => {
+ bug!()
+ }
+ }
+ }
+
+ // Expand trait aliases recursively and check that only one regular (non-auto) trait
+ // is used and no 'maybe' bounds are used.
+ let expanded_traits =
+ traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b)));
+
+ let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
+ .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
+ .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
+ if regular_traits.len() > 1 {
+ let first_trait = &regular_traits[0];
+ let additional_trait = &regular_traits[1];
+ let mut err = struct_span_err!(
+ tcx.sess,
+ additional_trait.bottom().1,
+ E0225,
+ "only auto traits can be used as additional traits in a trait object"
+ );
+ additional_trait.label_with_exp_info(
+ &mut err,
+ "additional non-auto trait",
+ "additional use",
+ );
+ first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
+ err.help(format!(
+ "consider creating a new trait with all of these as supertraits and using that \
+ trait here instead: `trait NewTrait: {} {{}}`",
+ regular_traits
+ .iter()
+ .map(|t| t.trait_ref().print_only_trait_path().to_string())
+ .collect::<Vec<_>>()
+ .join(" + "),
+ ));
+ err.note(
+ "auto-traits like `Send` and `Sync` are traits that have special properties; \
+ for more information on them, visit \
+ <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
+ );
+ err.emit();
+ }
+
+ if regular_traits.is_empty() && auto_traits.is_empty() {
+ let trait_alias_span = trait_bounds
+ .iter()
+ .map(|&(trait_ref, _, _)| trait_ref.def_id())
+ .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
+ .map(|trait_ref| tcx.def_span(trait_ref));
+ let reported =
+ tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
+ return Ty::new_error(tcx, reported);
+ }
+
+ // Check that there are no gross object safety violations;
+ // most importantly, that the supertraits don't contain `Self`,
+ // to avoid ICEs.
+ for item in &regular_traits {
+ let object_safety_violations =
+ astconv_object_safety_violations(tcx, item.trait_ref().def_id());
+ if !object_safety_violations.is_empty() {
+ let reported = report_object_safety_error(
+ tcx,
+ span,
+ item.trait_ref().def_id(),
+ &object_safety_violations,
+ )
+ .emit();
+ return Ty::new_error(tcx, reported);
+ }
+ }
+
+ // Use a `BTreeSet` to keep output in a more consistent order.
+ let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
+
+ let regular_traits_refs_spans = trait_bounds
+ .into_iter()
+ .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
+
+ for (base_trait_ref, span, constness) in regular_traits_refs_spans {
+ assert_eq!(constness, ty::BoundConstness::NotConst);
+ let base_pred: ty::Predicate<'tcx> = base_trait_ref.to_predicate(tcx);
+ for pred in traits::elaborate(tcx, [base_pred]) {
+ debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", pred);
+
+ let bound_predicate = pred.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+ let pred = bound_predicate.rebind(pred);
+ associated_types.entry(span).or_default().extend(
+ tcx.associated_items(pred.def_id())
+ .in_definition_order()
+ .filter(|item| item.kind == ty::AssocKind::Type)
+ .filter(|item| item.opt_rpitit_info.is_none())
+ .map(|item| item.def_id),
+ );
+ }
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
+ let pred = bound_predicate.rebind(pred);
+ // A `Self` within the original bound will be substituted with a
+ // `trait_object_dummy_self`, so check for that.
+ let references_self = match pred.skip_binder().term.unpack() {
+ ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
+ ty::TermKind::Const(c) => {
+ c.ty().walk().any(|arg| arg == dummy_self.into())
+ }
+ };
+
+ // If the projection output contains `Self`, force the user to
+ // elaborate it explicitly to avoid a lot of complexity.
+ //
+ // The "classically useful" case is the following:
+ // ```
+ // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
+ // type MyOutput;
+ // }
+ // ```
+ //
+ // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
+ // but actually supporting that would "expand" to an infinitely-long type
+ // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
+ //
+ // Instead, we force the user to write
+ // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
+ // the discussion in #56288 for alternatives.
+ if !references_self {
+ // Include projections defined on supertraits.
+ projection_bounds.push((pred, span));
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+
+ // `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`.
+ // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated
+ // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a
+ // corresponding `Projection` clause
+ for def_ids in associated_types.values_mut() {
+ for (projection_bound, span) in &projection_bounds {
+ let def_id = projection_bound.projection_def_id();
+ def_ids.remove(&def_id);
+ if tcx.generics_require_sized_self(def_id) {
+ tcx.emit_spanned_lint(
+ UNUSED_ASSOCIATED_TYPE_BOUNDS,
+ hir_id,
+ *span,
+ crate::errors::UnusedAssociatedTypeBounds { span: *span },
+ );
+ }
+ }
+ // If the associated type has a `where Self: Sized` bound, we do not need to constrain the associated
+ // type in the `dyn Trait`.
+ def_ids.retain(|def_id| !tcx.generics_require_sized_self(def_id));
+ }
+
+ self.complain_about_missing_associated_types(
+ associated_types,
+ potential_assoc_types,
+ hir_trait_bounds,
+ );
+
+ // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
+ // `dyn Trait + Send`.
+ // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
+ // the bounds
+ let mut duplicates = FxHashSet::default();
+ auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
+ debug!("regular_traits: {:?}", regular_traits);
+ debug!("auto_traits: {:?}", auto_traits);
+
+ // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
+ let existential_trait_refs = regular_traits.iter().map(|i| {
+ i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
+ assert_eq!(trait_ref.self_ty(), dummy_self);
+
+ // Verify that `dummy_self` did not leak inside default type parameters. This
+ // could not be done at path creation, since we need to see through trait aliases.
+ let mut missing_type_params = vec![];
+ let mut references_self = false;
+ let generics = tcx.generics_of(trait_ref.def_id);
+ let substs: Vec<_> = trait_ref
+ .substs
+ .iter()
+ .enumerate()
+ .skip(1) // Remove `Self` for `ExistentialPredicate`.
+ .map(|(index, arg)| {
+ if arg == dummy_self.into() {
+ let param = &generics.params[index];
+ missing_type_params.push(param.name);
+ return Ty::new_misc_error(tcx).into();
+ } else if arg.walk().any(|arg| arg == dummy_self.into()) {
+ references_self = true;
+ return Ty::new_misc_error(tcx).into();
+ }
+ arg
+ })
+ .collect();
+ let substs = tcx.mk_substs(&substs);
+
+ let span = i.bottom().1;
+ let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
+ hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
+ && hir_bound.span.contains(span)
+ });
+ self.complain_about_missing_type_params(
+ missing_type_params,
+ trait_ref.def_id,
+ span,
+ empty_generic_args,
+ );
+
+ if references_self {
+ let def_id = i.bottom().0.def_id();
+ let mut err = struct_span_err!(
+ tcx.sess,
+ i.bottom().1,
+ E0038,
+ "the {} `{}` cannot be made into an object",
+ tcx.def_descr(def_id),
+ tcx.item_name(def_id),
+ );
+ err.note(
+ rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
+ .error_msg(),
+ );
+ err.emit();
+ }
+
+ ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
+ })
+ });
+
+ let existential_projections = projection_bounds
+ .iter()
+ // We filter out traits that don't have `Self` as their self type above,
+ // we need to do the same for projections.
+ .filter(|(bound, _)| bound.skip_binder().self_ty() == dummy_self)
+ .map(|(bound, _)| {
+ bound.map_bound(|mut b| {
+ assert_eq!(b.projection_ty.self_ty(), dummy_self);
+
+ // Like for trait refs, verify that `dummy_self` did not leak inside default type
+ // parameters.
+ let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
+ if arg.walk().any(|arg| arg == dummy_self.into()) {
+ return true;
+ }
+ false
+ });
+ if references_self {
+ let guar = tcx.sess.delay_span_bug(
+ span,
+ "trait object projection bounds reference `Self`",
+ );
+ let substs: Vec<_> = b
+ .projection_ty
+ .substs
+ .iter()
+ .map(|arg| {
+ if arg.walk().any(|arg| arg == dummy_self.into()) {
+ return Ty::new_error(tcx, guar).into();
+ }
+ arg
+ })
+ .collect();
+ b.projection_ty.substs = tcx.mk_substs(&substs);
+ }
+
+ ty::ExistentialProjection::erase_self_ty(tcx, b)
+ })
+ });
+
+ let regular_trait_predicates = existential_trait_refs
+ .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
+ let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
+ ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
+ });
+ // N.b. principal, projections, auto traits
+ // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
+ let mut v = regular_trait_predicates
+ .chain(
+ existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
+ )
+ .chain(auto_trait_predicates)
+ .collect::<SmallVec<[_; 8]>>();
+ v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
+ v.dedup();
+ let existential_predicates = tcx.mk_poly_existential_predicates(&v);
+
+ // Use explicitly-specified region bound.
+ let region_bound = if !lifetime.is_elided() {
+ self.ast_region_to_region(lifetime, None)
+ } else {
+ self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
+ if tcx.named_bound_var(lifetime.hir_id).is_some() {
+ self.ast_region_to_region(lifetime, None)
+ } else {
+ self.re_infer(None, span).unwrap_or_else(|| {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0228,
+ "the lifetime bound for this object type cannot be deduced \
+ from context; please supply an explicit bound"
+ );
+ let e = if borrowed {
+ // We will have already emitted an error E0106 complaining about a
+ // missing named lifetime in `&dyn Trait`, so we elide this one.
+ err.delay_as_bug()
+ } else {
+ err.emit()
+ };
+ ty::Region::new_error(tcx, e)
+ })
+ }
+ })
+ };
+ debug!("region_bound: {:?}", region_bound);
+
+ let ty = Ty::new_dynamic(tcx, existential_predicates, region_bound, representation);
+ debug!("trait_object_type: {:?}", ty);
+ ty
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index d6d1498d7..c07ac35cb 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -73,7 +73,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
// NOTE: we may still need to normalize the built-in deref in case
// we have some type like `&<Ty as Trait>::Assoc`, since users of
// autoderef expect this type to have been structurally normalized.
- if self.infcx.tcx.trait_solver_next()
+ if self.infcx.next_trait_solver()
&& let ty::Alias(ty::Projection, _) = ty.kind()
{
let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
@@ -148,8 +148,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
return None;
}
- let (normalized_ty, obligations) =
- self.structurally_normalize(tcx.mk_projection(tcx.lang_items().deref_target()?, [ty]))?;
+ let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection(
+ tcx,
+ tcx.lang_items().deref_target()?,
+ [ty],
+ ))?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);
@@ -161,8 +164,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
&self,
ty: Ty<'tcx>,
) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
- let tcx = self.infcx.tcx;
- let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
+ let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.infcx);
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let normalized_ty = match self
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 686066abb..531100e1f 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -23,7 +23,7 @@ use rustc_span::Span;
/// include the self type (e.g., `trait_bounds`) but in others we do not
#[derive(Default, PartialEq, Eq, Clone, Debug)]
pub struct Bounds<'tcx> {
- pub predicates: Vec<(ty::Predicate<'tcx>, Span)>,
+ pub clauses: Vec<(ty::Clause<'tcx>, Span)>,
}
impl<'tcx> Bounds<'tcx> {
@@ -33,7 +33,8 @@ impl<'tcx> Bounds<'tcx> {
region: ty::PolyTypeOutlivesPredicate<'tcx>,
span: Span,
) {
- self.predicates.push((region.to_predicate(tcx), span));
+ self.clauses
+ .push((region.map_bound(|p| ty::ClauseKind::TypeOutlives(p)).to_predicate(tcx), span));
}
pub fn push_trait_bound(
@@ -44,9 +45,11 @@ impl<'tcx> Bounds<'tcx> {
constness: ty::BoundConstness,
polarity: ty::ImplPolarity,
) {
- self.predicates.push((
+ self.clauses.push((
trait_ref
- .map_bound(|trait_ref| ty::TraitPredicate { trait_ref, constness, polarity })
+ .map_bound(|trait_ref| {
+ ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, constness, polarity })
+ })
.to_predicate(tcx),
span,
));
@@ -58,17 +61,20 @@ impl<'tcx> Bounds<'tcx> {
projection: ty::PolyProjectionPredicate<'tcx>,
span: Span,
) {
- self.predicates.push((projection.to_predicate(tcx), span));
+ self.clauses.push((
+ projection.map_bound(|proj| ty::ClauseKind::Projection(proj)).to_predicate(tcx),
+ span,
+ ));
}
pub fn push_sized(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) {
let sized_def_id = tcx.require_lang_item(LangItem::Sized, Some(span));
let trait_ref = ty::TraitRef::new(tcx, sized_def_id, [ty]);
// Preferable to put this obligation first, since we report better errors for sized ambiguity.
- self.predicates.insert(0, (trait_ref.without_const().to_predicate(tcx), span));
+ self.clauses.insert(0, (trait_ref.to_predicate(tcx), span));
}
- pub fn predicates(&self) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + '_ {
- self.predicates.iter().cloned()
+ pub fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
+ self.clauses.iter().cloned()
}
}
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 3b2c052e8..120545c8e 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -177,7 +177,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
}
// Generic statics are rejected, but we still reach this case.
Err(e) => {
- tcx.sess.delay_span_bug(span, e.to_string());
+ tcx.sess.delay_span_bug(span, format!("{e:?}"));
return;
}
};
@@ -224,7 +224,8 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
return;
}
- check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
+
+ let _ = check_opaque_meets_bounds(tcx, item.owner_id.def_id, span, &origin);
}
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
@@ -299,7 +300,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
}
}
- if let ItemKind::OpaqueTy(hir::OpaqueTy {
+ if let ItemKind::OpaqueTy(&hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
in_trait,
..
@@ -307,9 +308,9 @@ pub(super) fn check_opaque_for_inheriting_lifetimes(
{
let substs = InternalSubsts::identity_for_item(tcx, def_id);
let opaque_identity_ty = if in_trait && !tcx.lower_impl_trait_in_trait_to_assoc_ty() {
- tcx.mk_projection(def_id.to_def_id(), substs)
+ Ty::new_projection(tcx, def_id.to_def_id(), substs)
} else {
- tcx.mk_opaque(def_id.to_def_id(), substs)
+ Ty::new_opaque(tcx, def_id.to_def_id(), substs)
};
let mut visitor = ProhibitOpaqueVisitor {
opaque_identity_ty,
@@ -395,7 +396,7 @@ fn check_opaque_meets_bounds<'tcx>(
def_id: LocalDefId,
span: Span,
origin: &hir::OpaqueTyOrigin,
-) {
+) -> Result<(), ErrorGuaranteed> {
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias { .. } => tcx.impl_trait_parent(def_id),
@@ -409,7 +410,7 @@ fn check_opaque_meets_bounds<'tcx>(
let ocx = ObligationCtxt::new(&infcx);
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
- let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+ let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), substs);
// `ReErased` regions appear in the "parent_substs" of closures/generators.
// We're ignoring them here and replacing them with fresh region variables.
@@ -429,24 +430,26 @@ fn check_opaque_meets_bounds<'tcx>(
Ok(()) => {}
Err(ty_err) => {
let ty_err = ty_err.to_string(tcx);
- tcx.sess.delay_span_bug(
+ return Err(tcx.sess.delay_span_bug(
span,
format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
- );
+ ));
}
}
// Additionally require the hidden type to be well-formed with only the generics of the opaque type.
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
// hidden type is well formed even without those bounds.
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into()));
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into())));
ocx.register_obligation(Obligation::new(tcx, misc_cause, param_env, predicate));
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
- infcx.err_ctxt().report_fulfillment_errors(&errors);
+ let guar = infcx.err_ctxt().report_fulfillment_errors(&errors);
+ return Err(guar);
}
match origin {
// Checked when type checking the function containing them.
@@ -460,14 +463,15 @@ fn check_opaque_meets_bounds<'tcx>(
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
// Can have different predicates to their defining use
hir::OpaqueTyOrigin::TyAlias { .. } => {
- let wf_tys = ocx.assumed_wf_types(param_env, span, def_id);
+ let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, wf_tys);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
- let _ = ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env);
+ ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
}
}
// Clean up after ourselves
let _ = infcx.take_opaque_types();
+ Ok(())
}
fn is_enum_of_nonnullable_ptr<'tcx>(
@@ -562,8 +566,8 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
check_union(tcx, id.owner_id.def_id);
}
DefKind::OpaqueTy => {
- let opaque = tcx.hir().expect_item(id.owner_id.def_id).expect_opaque_ty();
- if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = opaque.origin
+ let origin = tcx.opaque_type_origin(id.owner_id.def_id);
+ if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
&& let hir::Node::TraitItem(trait_item) = tcx.hir().get_by_def_id(fn_def_id)
&& let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn()
{
@@ -704,7 +708,7 @@ pub(super) fn check_specialization_validity<'tcx>(
// grandparent. In that case, if parent is a `default impl`, inherited items use the
// "defaultness" from the grandparent, else they are final.
None => {
- if tcx.impl_defaultness(parent_impl.def_id()).is_default() {
+ if tcx.defaultness(parent_impl.def_id()).is_default() {
None
} else {
Some(Err(parent_impl.def_id()))
@@ -718,7 +722,14 @@ pub(super) fn check_specialization_validity<'tcx>(
let result = opt_result.unwrap_or(Ok(()));
if let Err(parent_impl) = result {
- report_forbidden_specialization(tcx, impl_item, parent_impl);
+ if !tcx.is_impl_trait_in_trait(impl_item) {
+ report_forbidden_specialization(tcx, impl_item, parent_impl);
+ } else {
+ tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!("parent item: {:?} not marked as default", parent_impl),
+ );
+ }
}
}
@@ -803,7 +814,7 @@ fn check_impl_items_against_trait<'tcx>(
.as_ref()
.is_some_and(|node_item| node_item.item.defaultness(tcx).has_value());
- if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
+ if !is_implemented && tcx.defaultness(impl_id).is_final() {
missing_items.push(tcx.associated_item(trait_item_id));
}
@@ -1481,7 +1492,9 @@ fn opaque_type_cycle_error(
}
for closure_def_id in visitor.closures {
- let Some(closure_local_did) = closure_def_id.as_local() else { continue; };
+ let Some(closure_local_did) = closure_def_id.as_local() else {
+ continue;
+ };
let typeck_results = tcx.typeck(closure_local_did);
let mut label_match = |ty: Ty<'_>, span| {
@@ -1549,7 +1562,7 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
.with_opaque_type_inference(DefiningAnchor::Bind(def_id))
.build();
- let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+ let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx);
for (predicate, cause) in generator_interior_predicates {
let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
fulfillment_cx.register_predicate_obligation(&infcx, obligation);
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 8bf1e0e84..22e576e34 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -19,12 +19,13 @@ use rustc_middle::ty::{
self, InternalSubsts, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
};
+use std::borrow::Cow;
use std::iter;
/// Checks that a method from an impl conforms to the signature of
@@ -44,12 +45,7 @@ pub(super) fn compare_impl_method<'tcx>(
debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
let _: Result<_, ErrorGuaranteed> = try {
- compare_self_type(tcx, impl_m, trait_m, impl_trait_ref)?;
- compare_number_of_generics(tcx, impl_m, trait_m, false)?;
- compare_generic_param_kinds(tcx, impl_m, trait_m, false)?;
- compare_number_of_method_arguments(tcx, impl_m, trait_m)?;
- compare_synthetic_generics(tcx, impl_m, trait_m)?;
- compare_asyncness(tcx, impl_m, trait_m)?;
+ check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
compare_method_predicate_entailment(
tcx,
impl_m,
@@ -60,6 +56,26 @@ pub(super) fn compare_impl_method<'tcx>(
};
}
+/// Checks a bunch of different properties of the impl/trait methods for
+/// compatibility, such as asyncness, number of argument, self receiver kind,
+/// and number of early- and late-bound generics.
+fn check_method_is_structurally_compatible<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: ty::AssocItem,
+ trait_m: ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ delay: bool,
+) -> Result<(), ErrorGuaranteed> {
+ compare_self_type(tcx, impl_m, trait_m, impl_trait_ref, delay)?;
+ compare_number_of_generics(tcx, impl_m, trait_m, delay)?;
+ compare_generic_param_kinds(tcx, impl_m, trait_m, delay)?;
+ compare_number_of_method_arguments(tcx, impl_m, trait_m, delay)?;
+ compare_synthetic_generics(tcx, impl_m, trait_m, delay)?;
+ compare_asyncness(tcx, impl_m, trait_m, delay)?;
+ check_region_bounds_on_impl_item(tcx, impl_m, trait_m, delay)?;
+ Ok(())
+}
+
/// This function is best explained by example. Consider a trait with it's implementation:
///
/// ```rust
@@ -176,9 +192,6 @@ fn compare_method_predicate_entailment<'tcx>(
let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
- // Check region bounds.
- check_region_bounds_on_impl_item(tcx, impl_m, trait_m, false)?;
-
// Create obligations for each predicate declared by the impl
// definition in the context of the trait's parameter
// environment. We can't just use `impl_env.caller_bounds`,
@@ -207,7 +220,7 @@ fn compare_method_predicate_entailment<'tcx>(
// the new hybrid bounds we computed.
let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(
- tcx.mk_predicates(&hybrid_preds.predicates),
+ tcx.mk_clauses(&hybrid_preds.predicates),
Reveal::UserFacing,
hir::Constness::NotConst,
);
@@ -258,7 +271,7 @@ fn compare_method_predicate_entailment<'tcx>(
infer::HigherRankedType,
tcx.fn_sig(impl_m.def_id).subst_identity(),
);
- let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
+ let unnormalized_impl_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig));
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
@@ -275,7 +288,7 @@ fn compare_method_predicate_entailment<'tcx>(
// We also have to add the normalized trait signature
// as we don't normalize during implied bounds computation.
wf_tys.extend(trait_sig.inputs_and_output.iter());
- let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
+ let trait_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(trait_sig));
debug!("compare_impl_method: trait_fty={:?}", trait_fty);
@@ -301,14 +314,16 @@ fn compare_method_predicate_entailment<'tcx>(
return Err(emitted);
}
- if check_implied_wf == CheckImpliedWfMode::Check {
+ if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() {
// We need to check that the impl's args are well-formed given
// the hybrid param-env (impl + trait method where-clauses).
ocx.register_obligation(traits::Obligation::new(
infcx.tcx,
ObligationCause::dummy(),
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ unnormalized_impl_fty.into(),
+ ))),
));
}
@@ -471,7 +486,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for RemapLateBound<'_, 'tcx> {
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
if let ty::ReFree(fr) = *r {
- self.tcx.mk_re_free(
+ ty::Region::new_free(
+ self.tcx,
fr.scope,
self.mapping.get(&fr.bound_region).copied().unwrap_or(fr.bound_region),
)
@@ -532,6 +548,7 @@ fn compare_asyncness<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,
+ delay: bool,
) -> Result<(), ErrorGuaranteed> {
if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
match tcx.fn_sig(impl_m.def_id).skip_binder().skip_binder().output().kind() {
@@ -542,11 +559,14 @@ fn compare_asyncness<'tcx>(
// We don't know if it's ok, but at least it's already an error.
}
_ => {
- return Err(tcx.sess.emit_err(crate::errors::AsyncTraitImplShouldBeAsync {
- span: tcx.def_span(impl_m.def_id),
- method_name: trait_m.name,
- trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
- }));
+ return Err(tcx
+ .sess
+ .create_err(crate::errors::AsyncTraitImplShouldBeAsync {
+ span: tcx.def_span(impl_m.def_id),
+ method_name: trait_m.name,
+ trait_item_span: tcx.hir().span_if_local(trait_m.def_id),
+ })
+ .emit_unless(delay));
}
};
}
@@ -600,9 +620,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
// First, check a few of the same things as `compare_impl_method`,
// just so we don't ICE during substitution later.
- compare_number_of_generics(tcx, impl_m, trait_m, true)?;
- compare_generic_param_kinds(tcx, impl_m, trait_m, true)?;
- check_region_bounds_on_impl_item(tcx, impl_m, trait_m, true)?;
+ check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?;
let trait_to_impl_substs = impl_trait_ref.substs;
@@ -633,11 +651,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
let impl_sig = ocx.normalize(
&norm_cause,
param_env,
- infcx.instantiate_binder_with_fresh_vars(
- return_span,
- infer::HigherRankedType,
- tcx.fn_sig(impl_m.def_id).subst_identity(),
- ),
+ tcx.liberate_late_bound_regions(impl_m.def_id, tcx.fn_sig(impl_m.def_id).subst_identity()),
);
impl_sig.error_reported()?;
let impl_return_ty = impl_sig.output();
@@ -647,18 +661,21 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
// them with inference variables.
// We will use these inference variables to collect the hidden types of RPITITs.
let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id);
- let unnormalized_trait_sig = tcx
- .liberate_late_bound_regions(
- impl_m.def_id,
+ let unnormalized_trait_sig = infcx
+ .instantiate_binder_with_fresh_vars(
+ return_span,
+ infer::HigherRankedType,
tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
)
.fold_with(&mut collector);
- debug_assert_ne!(
- collector.types.len(),
- 0,
- "expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`"
- );
+ if !unnormalized_trait_sig.output().references_error() {
+ debug_assert_ne!(
+ collector.types.len(),
+ 0,
+ "expect >1 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`"
+ );
+ }
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
trait_sig.error_reported()?;
@@ -684,7 +701,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
&cause,
hir.get_if_local(impl_m.def_id)
.and_then(|node| node.fn_decl())
- .map(|decl| (decl.output.span(), "return type in trait".to_owned())),
+ .map(|decl| (decl.output.span(), Cow::from("return type in trait"))),
Some(infer::ValuePairs::Terms(ExpectedFound {
expected: trait_return_ty.into(),
found: impl_return_ty.into(),
@@ -742,15 +759,17 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
let mut collected_tys = FxHashMap::default();
for (def_id, (ty, substs)) in collected_types {
- match infcx.fully_resolve(ty) {
- Ok(ty) => {
+ match infcx.fully_resolve((ty, substs)) {
+ Ok((ty, substs)) => {
// `ty` contains free regions that we created earlier while liberating the
// trait fn signature. However, projection normalization expects `ty` to
// contains `def_id`'s early-bound regions.
let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
debug!(?id_substs, ?substs);
- let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
- std::iter::zip(substs, id_substs).collect();
+ let map: FxHashMap<_, _> = std::iter::zip(substs, id_substs)
+ .skip(tcx.generics_of(trait_m.def_id).count())
+ .filter_map(|(a, b)| Some((a.as_region()?, b.as_region()?)))
+ .collect();
debug!(?map);
// NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound
@@ -775,33 +794,27 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
// same generics.
let num_trait_substs = trait_to_impl_substs.len();
let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len();
- let ty = tcx.fold_regions(ty, |region, _| {
- match region.kind() {
- // Remap all free regions, which correspond to late-bound regions in the function.
- ty::ReFree(_) => {}
- // Remap early-bound regions as long as they don't come from the `impl` itself.
- ty::ReEarlyBound(ebr) if tcx.parent(ebr.def_id) != impl_m.container_id(tcx) => {}
- _ => return region,
- }
- let Some(ty::ReEarlyBound(e)) = map.get(&region.into()).map(|r| r.expect_region().kind())
- else {
- return tcx.mk_re_error_with_message(return_span, "expected ReFree to map to ReEarlyBound")
- };
- tcx.mk_re_early_bound(ty::EarlyBoundRegion {
- def_id: e.def_id,
- name: e.name,
- index: (e.index as usize - num_trait_substs + num_impl_substs) as u32,
- })
- });
- debug!(%ty);
- collected_tys.insert(def_id, ty::EarlyBinder(ty));
+ let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions {
+ tcx,
+ map,
+ num_trait_substs,
+ num_impl_substs,
+ def_id,
+ impl_def_id: impl_m.container_id(tcx),
+ ty,
+ return_span,
+ }) {
+ Ok(ty) => ty,
+ Err(guar) => Ty::new_error(tcx, guar),
+ };
+ collected_tys.insert(def_id, ty::EarlyBinder::bind(ty));
}
Err(err) => {
let reported = tcx.sess.delay_span_bug(
return_span,
format!("could not fully resolve: {ty} => {err:?}"),
);
- collected_tys.insert(def_id, ty::EarlyBinder(tcx.ty_error(reported)));
+ collected_tys.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported)));
}
}
}
@@ -877,6 +890,97 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
}
}
+struct RemapHiddenTyRegions<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ map: FxHashMap<ty::Region<'tcx>, ty::Region<'tcx>>,
+ num_trait_substs: usize,
+ num_impl_substs: usize,
+ def_id: DefId,
+ impl_def_id: DefId,
+ ty: Ty<'tcx>,
+ return_span: Span,
+}
+
+impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
+ type Error = ErrorGuaranteed;
+
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ if let ty::Alias(ty::Opaque, ty::AliasTy { substs, def_id, .. }) = *t.kind() {
+ let mut mapped_substs = Vec::with_capacity(substs.len());
+ for (arg, v) in std::iter::zip(substs, self.tcx.variances_of(def_id)) {
+ mapped_substs.push(match (arg.unpack(), v) {
+ // Skip uncaptured opaque substs
+ (ty::GenericArgKind::Lifetime(_), ty::Bivariant) => arg,
+ _ => arg.try_fold_with(self)?,
+ });
+ }
+ Ok(Ty::new_opaque(self.tcx, def_id, self.tcx.mk_substs(&mapped_substs)))
+ } else {
+ t.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_region(
+ &mut self,
+ region: ty::Region<'tcx>,
+ ) -> Result<ty::Region<'tcx>, Self::Error> {
+ match region.kind() {
+ // Remap all free regions, which correspond to late-bound regions in the function.
+ ty::ReFree(_) => {}
+ // Remap early-bound regions as long as they don't come from the `impl` itself,
+ // in which case we don't really need to renumber them.
+ ty::ReEarlyBound(ebr) if self.tcx.parent(ebr.def_id) != self.impl_def_id => {}
+ _ => return Ok(region),
+ }
+
+ let e = if let Some(region) = self.map.get(&region) {
+ if let ty::ReEarlyBound(e) = region.kind() { e } else { bug!() }
+ } else {
+ let guar = match region.kind() {
+ ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. })
+ | ty::ReFree(ty::FreeRegion {
+ bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
+ ..
+ }) => {
+ let return_span = if let ty::Alias(ty::Opaque, opaque_ty) = self.ty.kind() {
+ self.tcx.def_span(opaque_ty.def_id)
+ } else {
+ self.return_span
+ };
+ self.tcx
+ .sess
+ .struct_span_err(
+ return_span,
+ "return type captures more lifetimes than trait definition",
+ )
+ .span_label(self.tcx.def_span(def_id), "this lifetime was captured")
+ .span_note(
+ self.tcx.def_span(self.def_id),
+ "hidden type must only reference lifetimes captured by this impl trait",
+ )
+ .note(format!("hidden type inferred to be `{}`", self.ty))
+ .emit()
+ }
+ _ => self.tcx.sess.delay_span_bug(DUMMY_SP, "should've been able to remap region"),
+ };
+ return Err(guar);
+ };
+
+ Ok(ty::Region::new_early_bound(
+ self.tcx,
+ ty::EarlyBoundRegion {
+ def_id: e.def_id,
+ name: e.name,
+ index: (e.index as usize - self.num_trait_substs + self.num_impl_substs) as u32,
+ },
+ ))
+ }
+}
+
fn report_trait_method_mismatch<'tcx>(
infcx: &InferCtxt<'tcx>,
mut cause: ObligationCause<'tcx>,
@@ -901,7 +1005,7 @@ fn report_trait_method_mismatch<'tcx>(
if trait_m.fn_has_self_parameter =>
{
let ty = trait_sig.inputs()[0];
- let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) {
+ let sugg = match ExplicitSelf::determine(ty, |ty| ty == impl_trait_ref.self_ty()) {
ExplicitSelf::ByValue => "self".to_owned(),
ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
@@ -963,7 +1067,7 @@ fn report_trait_method_mismatch<'tcx>(
infcx.err_ctxt().note_type_err(
&mut diag,
&cause,
- trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
+ trait_err_span.map(|sp| (sp, Cow::from("type in trait"))),
Some(infer::ValuePairs::Sigs(ExpectedFound { expected: trait_sig, found: impl_sig })),
terr,
false,
@@ -1095,6 +1199,7 @@ fn compare_self_type<'tcx>(
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
+ delay: bool,
) -> Result<(), ErrorGuaranteed> {
// Try to give more informative error messages about self typing
// mismatches. Note that any mismatch will also be detected
@@ -1143,7 +1248,7 @@ fn compare_self_type<'tcx>(
} else {
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
- return Err(err.emit());
+ return Err(err.emit_unless(delay));
}
(true, false) => {
@@ -1164,7 +1269,7 @@ fn compare_self_type<'tcx>(
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
- return Err(err.emit());
+ return Err(err.emit_unless(delay));
}
}
@@ -1214,7 +1319,7 @@ fn compare_number_of_generics<'tcx>(
// has mismatched type or const generic arguments, then the method that it's
// inheriting the generics from will also have mismatched arguments, and
// we'll report an error for that instead. Delay a bug for safety, though.
- if tcx.opt_rpitit_info(trait_.def_id).is_some() {
+ if trait_.opt_rpitit_info.is_some() {
return Err(tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
"errors comparing numbers of generics of trait/impl functions were not emitted",
@@ -1350,6 +1455,7 @@ fn compare_number_of_method_arguments<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,
+ delay: bool,
) -> Result<(), ErrorGuaranteed> {
let impl_m_fty = tcx.fn_sig(impl_m.def_id);
let trait_m_fty = tcx.fn_sig(trait_m.def_id);
@@ -1420,7 +1526,7 @@ fn compare_number_of_method_arguments<'tcx>(
),
);
- return Err(err.emit());
+ return Err(err.emit_unless(delay));
}
Ok(())
@@ -1430,6 +1536,7 @@ fn compare_synthetic_generics<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: ty::AssocItem,
trait_m: ty::AssocItem,
+ delay: bool,
) -> Result<(), ErrorGuaranteed> {
// FIXME(chrisvittal) Clean up this function, list of FIXME items:
// 1. Better messages for the span labels
@@ -1549,7 +1656,7 @@ fn compare_synthetic_generics<'tcx>(
}
_ => unreachable!(),
}
- error_found = Some(err.emit());
+ error_found = Some(err.emit_unless(delay));
}
}
if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
@@ -1731,7 +1838,7 @@ pub(super) fn compare_impl_const_raw(
infcx.err_ctxt().note_type_err(
&mut diag,
&cause,
- trait_c_span.map(|span| (span, "type in trait".to_owned())),
+ trait_c_span.map(|span| (span, Cow::from("type in trait"))),
Some(infer::ValuePairs::Terms(ExpectedFound {
expected: trait_ty.into(),
found: impl_ty.into(),
@@ -1814,7 +1921,7 @@ fn compare_type_predicate_entailment<'tcx>(
let impl_ty_span = tcx.def_span(impl_ty_def_id);
let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let param_env = ty::ParamEnv::new(
- tcx.mk_predicates(&hybrid_preds.predicates),
+ tcx.mk_clauses(&hybrid_preds.predicates),
Reveal::UserFacing,
hir::Constness::NotConst,
);
@@ -1922,7 +2029,8 @@ pub(super) fn check_type_bounds<'tcx>(
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Ty(kind);
bound_vars.push(bound_var);
- tcx.mk_bound(
+ Ty::new_bound(
+ tcx,
ty::INNERMOST,
ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
)
@@ -1932,7 +2040,8 @@ pub(super) fn check_type_bounds<'tcx>(
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Region(kind);
bound_vars.push(bound_var);
- tcx.mk_re_late_bound(
+ ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
)
@@ -1941,11 +2050,10 @@ pub(super) fn check_type_bounds<'tcx>(
GenericParamDefKind::Const { .. } => {
let bound_var = ty::BoundVariableKind::Const;
bound_vars.push(bound_var);
- tcx.mk_const(
- ty::ConstKind::Bound(
- ty::INNERMOST,
- ty::BoundVar::from_usize(bound_vars.len() - 1),
- ),
+ ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
@@ -1989,7 +2097,7 @@ pub(super) fn check_type_bounds<'tcx>(
.to_predicate(tcx),
),
};
- ty::ParamEnv::new(tcx.mk_predicates(&predicates), Reveal::UserFacing, param_env.constness())
+ ty::ParamEnv::new(tcx.mk_clauses(&predicates), Reveal::UserFacing, param_env.constness())
};
debug!(?normalize_param_env);
@@ -2003,7 +2111,7 @@ pub(super) fn check_type_bounds<'tcx>(
// A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
// span for an impl's associated type. Instead, for these, use the def_span for the synthesized
// associated type.
- let impl_ty_span = if tcx.opt_rpitit_info(impl_ty.def_id).is_some() {
+ let impl_ty_span = if impl_ty.opt_rpitit_info.is_some() {
tcx.def_span(impl_ty_def_id)
} else {
match tcx.hir().get_by_def_id(impl_ty_def_id) {
@@ -2015,7 +2123,7 @@ pub(super) fn check_type_bounds<'tcx>(
_ => bug!(),
}
};
- let assumed_wf_types = ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty_def_id);
+ let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl_ty_def_id)?;
let normalize_cause = ObligationCause::new(
impl_ty_span,
diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index e0ba255cc..13d1abe2a 100644
--- a/compiler/rustc_hir_analysis/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -128,7 +128,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
// We don't need to normalize this param-env or anything, since we're only
// substituting it with free params, so no additional param-env normalization
// can occur on top of what has been done in the param_env query itself.
- let param_env = ty::EarlyBinder(tcx.param_env(adt_def_id))
+ let param_env = ty::EarlyBinder::bind(tcx.param_env(adt_def_id))
.subst(tcx, adt_to_impl_substs)
.with_constness(tcx.constness(drop_impl_def_id));
@@ -183,7 +183,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
}
RegionResolutionError::SubSupConflict(_, _, _, a, _, b, _) => format!("{b}: {a}"),
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
- format!("{b}: {a}", a = tcx.mk_re_var(a))
+ format!("{b}: {a}", a = ty::Region::new_var(tcx, a))
}
};
guar = Some(
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index e8785235c..1248f991c 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -11,7 +11,7 @@ use hir::def_id::DefId;
use rustc_errors::{struct_span_err, DiagnosticMessage};
use rustc_hir as hir;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::spec::abi::Abi;
@@ -53,14 +53,14 @@ fn equate_intrinsic_type<'tcx>(
&& gen_count_ok(own_counts.types, n_tps, "type")
&& gen_count_ok(own_counts.consts, 0, "const")
{
- let fty = tcx.mk_fn_ptr(sig);
+ let fty = Ty::new_fn_ptr(tcx, sig);
let it_def_id = it.owner_id.def_id;
let cause = ObligationCause::new(it.span, it_def_id, ObligationCauseCode::IntrinsicType);
require_same_types(
tcx,
&cause,
ty::ParamEnv::empty(), // FIXME: do all intrinsics have an empty param env?
- tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id).subst_identity()),
+ Ty::new_fn_ptr(tcx, tcx.fn_sig(it.owner_id).subst_identity()),
fty,
);
}
@@ -121,10 +121,11 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir
if has_safe_attr != is_in_list {
tcx.sess.struct_span_err(
tcx.def_span(intrinsic_id),
- DiagnosticMessage::Str(format!(
- "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
- tcx.item_name(intrinsic_id)
- ))).emit();
+ DiagnosticMessage::from(format!(
+ "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
+ tcx.item_name(intrinsic_id)
+ )
+ )).emit();
}
is_in_list
@@ -133,7 +134,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir
/// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
/// and in `library/core/src/intrinsics.rs`.
pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
- let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
+ let param = |n| Ty::new_param(tcx, n, Symbol::intern(&format!("P{}", n)));
let intrinsic_id = it.owner_id.to_def_id();
let intrinsic_name = tcx.item_name(intrinsic_id);
let name_str = intrinsic_name.as_str();
@@ -144,16 +145,18 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
]);
let mk_va_list_ty = |mutbl| {
tcx.lang_items().va_list().map(|did| {
- let region = tcx.mk_re_late_bound(
+ let region = ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) },
);
- let env_region = tcx.mk_re_late_bound(
+ let env_region = ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv },
);
let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
- (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
+ (Ty::new_ref(tcx, env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty)
})
};
@@ -165,15 +168,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let (n_tps, inputs, output) = match split[1] {
"cxchg" | "cxchgweak" => (
1,
- vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)],
- tcx.mk_tup(&[param(0), tcx.types.bool]),
+ vec![Ty::new_mut_ptr(tcx, param(0)), param(0), param(0)],
+ Ty::new_tup(tcx, &[param(0), tcx.types.bool]),
),
- "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
- "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+ "load" => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
+ "store" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)),
"xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax"
- | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)),
- "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()),
+ | "umin" => (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], param(0)),
+ "fence" | "singlethreadfence" => (0, Vec::new(), Ty::new_unit(tcx)),
op => {
tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op });
return;
@@ -185,19 +188,19 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let (n_tps, inputs, output) = match intrinsic_name {
sym::abort => (0, Vec::new(), tcx.types.never),
sym::unreachable => (0, Vec::new(), tcx.types.never),
- sym::breakpoint => (0, Vec::new(), tcx.mk_unit()),
+ sym::breakpoint => (0, Vec::new(), Ty::new_unit(tcx)),
sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => {
(1, Vec::new(), tcx.types.usize)
}
sym::size_of_val | sym::min_align_of_val => {
- (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize)
+ (1, vec![Ty::new_imm_ptr(tcx, param(0))], tcx.types.usize)
}
sym::rustc_peek => (1, vec![param(0)], param(0)),
sym::caller_location => (0, vec![], tcx.caller_location_ty()),
sym::assert_inhabited
| sym::assert_zero_valid
- | sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()),
- sym::forget => (1, vec![param(0)], tcx.mk_unit()),
+ | sym::assert_mem_uninitialized_valid => (1, Vec::new(), Ty::new_unit(tcx)),
+ sym::forget => (1, vec![param(0)], Ty::new_unit(tcx)),
sym::transmute | sym::transmute_unchecked => (2, vec![param(0)], param(1)),
sym::prefetch_read_data
| sym::prefetch_write_data
@@ -205,75 +208,79 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
| sym::prefetch_write_instruction => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
tcx.types.i32,
],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
),
- sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()),
+ sym::drop_in_place => (1, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_unit(tcx)),
sym::needs_drop => (1, Vec::new(), tcx.types.bool),
- sym::type_name => (1, Vec::new(), tcx.mk_static_str()),
- sym::type_id => (1, Vec::new(), tcx.types.u64),
+ sym::type_name => (1, Vec::new(), Ty::new_static_str(tcx)),
+ sym::type_id => (1, Vec::new(), tcx.types.u128),
sym::offset => (2, vec![param(0), param(1)], param(0)),
sym::arith_offset => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
tcx.types.isize,
],
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
sym::option_payload_ptr => {
let option_def_id = tcx.require_lang_item(hir::LangItem::Option, None);
let p0 = param(0);
(
1,
- vec![tcx.mk_ptr(ty::TypeAndMut {
- ty: tcx.mk_adt(
- tcx.adt_def(option_def_id),
- tcx.mk_substs_from_iter([ty::GenericArg::from(p0)].into_iter()),
- ),
- mutbl: hir::Mutability::Not,
- })],
- tcx.mk_ptr(ty::TypeAndMut { ty: p0, mutbl: hir::Mutability::Not }),
+ vec![Ty::new_ptr(
+ tcx,
+ ty::TypeAndMut {
+ ty: Ty::new_adt(
+ tcx,
+ tcx.adt_def(option_def_id),
+ tcx.mk_substs_from_iter([ty::GenericArg::from(p0)].into_iter()),
+ ),
+ mutbl: hir::Mutability::Not,
+ },
+ )],
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: p0, mutbl: hir::Mutability::Not }),
)
}
sym::ptr_mask => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
tcx.types.usize,
],
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
sym::copy | sym::copy_nonoverlapping => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
tcx.types.usize,
],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
),
sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
tcx.types.usize,
],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
),
sym::write_bytes | sym::volatile_set_memory => (
1,
vec![
- tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }),
tcx.types.u8,
tcx.types.usize,
],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
),
sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32),
sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64),
@@ -321,10 +328,10 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
sym::roundevenf64 => (0, vec![tcx.types.f64], tcx.types.f64),
sym::volatile_load | sym::unaligned_volatile_load => {
- (1, vec![tcx.mk_imm_ptr(param(0))], param(0))
+ (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0))
}
sym::volatile_store | sym::unaligned_volatile_store => {
- (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit())
+ (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
}
sym::ctpop
@@ -336,28 +343,34 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
| sym::bitreverse => (1, vec![param(0)], param(0)),
sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
- (1, vec![param(0), param(0)], tcx.mk_tup(&[param(0), tcx.types.bool]))
+ (1, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
}
- sym::ptr_guaranteed_cmp => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
- }
+ sym::ptr_guaranteed_cmp => (
+ 1,
+ vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+ tcx.types.u8,
+ ),
sym::const_allocate => {
- (0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8))
+ (0, vec![tcx.types.usize, tcx.types.usize], Ty::new_mut_ptr(tcx, tcx.types.u8))
}
sym::const_deallocate => (
0,
- vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize],
- tcx.mk_unit(),
+ vec![Ty::new_mut_ptr(tcx, tcx.types.u8), tcx.types.usize, tcx.types.usize],
+ Ty::new_unit(tcx),
),
- sym::ptr_offset_from => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize)
- }
- sym::ptr_offset_from_unsigned => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize)
- }
+ sym::ptr_offset_from => (
+ 1,
+ vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+ tcx.types.isize,
+ ),
+ sym::ptr_offset_from_unsigned => (
+ 1,
+ vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
+ tcx.types.usize,
+ ),
sym::unchecked_div | sym::unchecked_rem | sym::exact_div => {
(1, vec![param(0), param(0)], param(0))
}
@@ -376,12 +389,14 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
}
sym::float_to_int_unchecked => (2, vec![param(0)], param(1)),
- sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()),
+ sym::assume => (0, vec![tcx.types.bool], Ty::new_unit(tcx)),
sym::likely => (0, vec![tcx.types.bool], tcx.types.bool),
sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool),
- sym::read_via_copy => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)),
- sym::write_via_move => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+ sym::read_via_copy => (1, vec![Ty::new_imm_ptr(tcx, param(0))], param(0)),
+ sym::write_via_move => {
+ (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
+ }
sym::discriminant_value => {
let assoc_items = tcx.associated_item_def_ids(
@@ -392,43 +407,47 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) };
(
1,
- vec![tcx.mk_imm_ref(tcx.mk_re_late_bound(ty::INNERMOST, br), param(0))],
- tcx.mk_projection(discriminant_def_id, tcx.mk_substs(&[param(0).into()])),
+ vec![Ty::new_imm_ref(
+ tcx,
+ ty::Region::new_late_bound(tcx, ty::INNERMOST, br),
+ param(0),
+ )],
+ Ty::new_projection(tcx, discriminant_def_id, tcx.mk_substs(&[param(0).into()])),
)
}
kw::Try => {
- let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
+ let mut_u8 = Ty::new_mut_ptr(tcx, tcx.types.u8);
let try_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig(
[mut_u8],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
false,
hir::Unsafety::Normal,
Abi::Rust,
));
let catch_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig(
[mut_u8, mut_u8],
- tcx.mk_unit(),
+ Ty::new_unit(tcx),
false,
hir::Unsafety::Normal,
Abi::Rust,
));
(
0,
- vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)],
+ vec![Ty::new_fn_ptr(tcx, try_fn_ty), mut_u8, Ty::new_fn_ptr(tcx, catch_fn_ty)],
tcx.types.i32,
)
}
sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) {
- Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()),
+ Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], Ty::new_unit(tcx)),
None => bug!("`va_list` language item needed for C-variadic intrinsics"),
},
sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) {
Some((va_list_ref_ty, va_list_ty)) => {
- let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty);
- (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit())
+ let va_list_ptr_ty = Ty::new_mut_ptr(tcx, va_list_ty);
+ (0, vec![va_list_ptr_ty, va_list_ref_ty], Ty::new_unit(tcx))
}
None => bug!("`va_list` language item needed for C-variadic intrinsics"),
},
@@ -438,11 +457,17 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
None => bug!("`va_list` language item needed for C-variadic intrinsics"),
},
- sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()),
+ sym::nontemporal_store => {
+ (1, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx))
+ }
sym::raw_eq => {
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(None) };
- let param_ty = tcx.mk_imm_ref(tcx.mk_re_late_bound(ty::INNERMOST, br), param(0));
+ let param_ty = Ty::new_imm_ref(
+ tcx,
+ ty::Region::new_late_bound(tcx, ty::INNERMOST, br),
+ param(0),
+ );
(1, vec![param_ty; 2], tcx.types.bool)
}
@@ -451,7 +476,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)),
sym::vtable_size | sym::vtable_align => {
- (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize)
+ (0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
}
other => {
@@ -470,7 +495,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let param = |n| {
let name = Symbol::intern(&format!("P{}", n));
- tcx.mk_ty_param(n, name)
+ Ty::new_param(tcx, n, name)
};
let name = it.ident.name;
@@ -512,7 +537,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)),
sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)),
sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)),
- sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
+ sym::simd_scatter => (3, vec![param(0), param(1), param(2)], Ty::new_unit(tcx)),
sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
sym::simd_cast
@@ -541,7 +566,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
name if name.as_str().starts_with("simd_shuffle") => {
match name.as_str()["simd_shuffle".len()..].parse() {
Ok(n) => {
- let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)];
+ let params = vec![param(0), param(0), Ty::new_array(tcx, tcx.types.u32, n)];
(2, params, param(1))
}
Err(_) => {
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 3971a4c01..ce2da7cb1 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -188,7 +188,7 @@ fn missing_items_err(
full_impl_span: Span,
) {
let missing_items =
- missing_items.iter().filter(|trait_item| tcx.opt_rpitit_info(trait_item.def_id).is_none());
+ missing_items.iter().filter(|trait_item| trait_item.opt_rpitit_info.is_none());
let missing_items_msg = missing_items
.clone()
@@ -296,7 +296,7 @@ fn default_body_is_unstable(
/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
fn bounds_from_generic_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
- predicates: impl IntoIterator<Item = (ty::Predicate<'tcx>, Span)>,
+ predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
) -> (String, String) {
let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default();
let mut projections = vec![];
@@ -304,7 +304,7 @@ fn bounds_from_generic_predicates<'tcx>(
debug!("predicate {:?}", predicate);
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::ClauseKind::Trait(trait_predicate) => {
let entry = types.entry(trait_predicate.self_ty()).or_default();
let def_id = trait_predicate.def_id();
if Some(def_id) != tcx.lang_items().sized_trait() {
@@ -313,7 +313,7 @@ fn bounds_from_generic_predicates<'tcx>(
entry.push(trait_predicate.def_id());
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(projection_pred)) => {
+ ty::ClauseKind::Projection(projection_pred) => {
projections.push(bound_predicate.rebind(projection_pred));
}
_ => {}
@@ -362,7 +362,7 @@ fn fn_sig_suggestion<'tcx>(
tcx: TyCtxt<'tcx>,
sig: ty::FnSig<'tcx>,
ident: Ident,
- predicates: impl IntoIterator<Item = (ty::Predicate<'tcx>, Span)>,
+ predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
assoc: ty::AssocItem,
) -> String {
let args = sig
@@ -403,7 +403,30 @@ fn fn_sig_suggestion<'tcx>(
.flatten()
.collect::<Vec<String>>()
.join(", ");
- let output = sig.output();
+ let mut output = sig.output();
+
+ let asyncness = if tcx.asyncness(assoc.def_id).is_async() {
+ output = if let ty::Alias(_, alias_ty) = *output.kind() {
+ tcx.explicit_item_bounds(alias_ty.def_id)
+ .subst_iter_copied(tcx, alias_ty.substs)
+ .find_map(|(bound, _)| bound.as_projection_clause()?.no_bound_vars()?.term.ty())
+ .unwrap_or_else(|| {
+ span_bug!(
+ ident.span,
+ "expected async fn to have `impl Future` output, but it returns {output}"
+ )
+ })
+ } else {
+ span_bug!(
+ ident.span,
+ "expected async fn to have `impl Future` output, but it returns {output}"
+ )
+ };
+ "async "
+ } else {
+ ""
+ };
+
let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
let unsafety = sig.unsafety.prefix_str();
@@ -414,7 +437,9 @@ fn fn_sig_suggestion<'tcx>(
// lifetimes between the `impl` and the `trait`, but this should be good enough to
// fill in a significant portion of the missing code, and other subsequent
// suggestions can help the user fix the code.
- format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
+ format!(
+ "{unsafety}{asyncness}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}"
+ )
}
pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
@@ -443,19 +468,16 @@ fn suggestion_signature<'tcx>(
);
match assoc.kind {
- ty::AssocKind::Fn => {
- // We skip the binder here because the binder would deanonymize all
- // late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
- // regions just fine, showing `fn(&MyType)`.
- fn_sig_suggestion(
- tcx,
- tcx.fn_sig(assoc.def_id).subst(tcx, substs).skip_binder(),
- assoc.ident(tcx),
- tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
- assoc,
- )
- }
+ ty::AssocKind::Fn => fn_sig_suggestion(
+ tcx,
+ tcx.liberate_late_bound_regions(
+ assoc.def_id,
+ tcx.fn_sig(assoc.def_id).subst(tcx, substs),
+ ),
+ assoc.ident(tcx),
+ tcx.predicates_of(assoc.def_id).instantiate_own(tcx, substs),
+ assoc,
+ ),
ty::AssocKind::Type => {
let (generics, where_clauses) = bounds_from_generic_predicates(
tcx,
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 6ab5556e9..5bd6fcb96 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -392,7 +392,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
// Manually recurse over closures and inline consts, because they are the only
// case of nested bodies that share the parent environment.
hir::ExprKind::Closure(&hir::Closure { body, .. })
- | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => {
+ | hir::ExprKind::ConstBlock(hir::ConstBlock { body, .. }) => {
let body = visitor.tcx.hir().body(body);
visitor.visit_body(body);
}
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index b403ee96b..d4748b7ef 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -15,7 +15,7 @@ use rustc_middle::mir::ConstraintCategory;
use rustc_middle::query::Providers;
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
- self, AdtKind, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+ self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_middle::ty::{GenericArgKind, InternalSubsts};
@@ -81,7 +81,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
self.tcx(),
cause,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))),
));
}
}
@@ -105,7 +105,12 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
}
f(&mut wfcx);
- let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id);
+ let assumed_wf_types = match wfcx.ocx.assumed_wf_types_and_report_errors(param_env, body_def_id)
+ {
+ Ok(wf_types) => wf_types,
+ Err(_guar) => return,
+ };
+
let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
let errors = wfcx.select_all_or_error();
@@ -217,10 +222,10 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
check_item_fn(tcx, def_id, item.ident, item.span, sig.decl);
}
hir::ItemKind::Static(ty, ..) => {
- check_item_type(tcx, def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid);
}
hir::ItemKind::Const(ty, ..) => {
- check_item_type(tcx, def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, UnsizedHandling::Forbid);
}
hir::ItemKind::Struct(_, ast_generics) => {
check_type_defn(tcx, item, false);
@@ -242,6 +247,12 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
// `ForeignItem`s are handled separately.
hir::ItemKind::ForeignMod { .. } => {}
+ hir::ItemKind::TyAlias(hir_ty, ..) => {
+ if tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() {
+ // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);`
+ check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow);
+ }
+ }
_ => {}
}
}
@@ -258,7 +269,9 @@ fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
hir::ForeignItemKind::Fn(decl, ..) => {
check_item_fn(tcx, def_id, item.ident, item.span, decl)
}
- hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true),
+ hir::ForeignItemKind::Static(ty, ..) => {
+ check_item_type(tcx, def_id, ty.span, UnsizedHandling::AllowIfForeignTail)
+ }
hir::ForeignItemKind::Type => (),
}
}
@@ -314,7 +327,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
// Gather the bounds with which all other items inside of this trait constrain the GAT.
// This is calculated by taking the intersection of the bounds that each item
// constrains the GAT with individually.
- let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
+ let mut new_required_bounds: Option<FxHashSet<ty::Clause<'_>>> = None;
for item in associated_items {
let item_def_id = item.id.owner_id;
// Skip our own GAT, since it does not constrain itself at all.
@@ -411,21 +424,17 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
let mut unsatisfied_bounds: Vec<_> = required_bounds
.into_iter()
.filter(|clause| match clause.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
- a,
- b,
- ))) => !region_known_to_outlive(
- tcx,
- gat_def_id.def_id,
- param_env,
- &FxIndexSet::default(),
- a,
- b,
- ),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
- a,
- b,
- ))) => !ty_known_to_outlive(
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
+ !region_known_to_outlive(
+ tcx,
+ gat_def_id.def_id,
+ param_env,
+ &FxIndexSet::default(),
+ a,
+ b,
+ )
+ }
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => !ty_known_to_outlive(
tcx,
gat_def_id.def_id,
param_env,
@@ -433,7 +442,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
a,
b,
),
- _ => bug!("Unexpected PredicateKind"),
+ _ => bug!("Unexpected ClauseKind"),
})
.map(|clause| clause.to_string())
.collect();
@@ -481,7 +490,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
fn augment_param_env<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- new_predicates: Option<&FxHashSet<ty::Predicate<'tcx>>>,
+ new_predicates: Option<&FxHashSet<ty::Clause<'tcx>>>,
) -> ty::ParamEnv<'tcx> {
let Some(new_predicates) = new_predicates else {
return param_env;
@@ -491,7 +500,7 @@ fn augment_param_env<'tcx>(
return param_env;
}
- let bounds = tcx.mk_predicates_from_iter(
+ let bounds = tcx.mk_clauses_from_iter(
param_env.caller_bounds().iter().chain(new_predicates.iter().cloned()),
);
// FIXME(compiler-errors): Perhaps there is a case where we need to normalize this
@@ -517,7 +526,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
wf_tys: &FxIndexSet<Ty<'tcx>>,
gat_def_id: LocalDefId,
gat_generics: &'tcx ty::Generics,
-) -> Option<FxHashSet<ty::Predicate<'tcx>>> {
+) -> Option<FxHashSet<ty::Clause<'tcx>>> {
// The bounds we that we would require from `to_check`
let mut bounds = FxHashSet::default();
@@ -552,22 +561,24 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
// our example, the type was `Self`, which will also be
// `Self` in the GAT.
let ty_param = gat_generics.param_at(*ty_idx, tcx);
- let ty_param = tcx.mk_ty_param(ty_param.index, ty_param.name);
+ let ty_param = Ty::new_param(tcx, ty_param.index, ty_param.name);
// Same for the region. In our example, 'a corresponds
// to the 'me parameter.
let region_param = gat_generics.param_at(*region_a_idx, tcx);
- let region_param = tcx.mk_re_early_bound(ty::EarlyBoundRegion {
- def_id: region_param.def_id,
- index: region_param.index,
- name: region_param.name,
- });
+ let region_param = ty::Region::new_early_bound(
+ tcx,
+ ty::EarlyBoundRegion {
+ def_id: region_param.def_id,
+ index: region_param.index,
+ name: region_param.name,
+ },
+ );
// The predicate we expect to see. (In our example,
// `Self: 'me`.)
- let clause = ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty_param, region_param),
- ));
- let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
- bounds.insert(clause);
+ bounds.insert(
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param))
+ .to_predicate(tcx),
+ );
}
}
@@ -593,24 +604,32 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
debug!("required clause: {region_a} must outlive {region_b}");
// Translate into the generic parameters of the GAT.
let region_a_param = gat_generics.param_at(*region_a_idx, tcx);
- let region_a_param = tcx.mk_re_early_bound(ty::EarlyBoundRegion {
- def_id: region_a_param.def_id,
- index: region_a_param.index,
- name: region_a_param.name,
- });
+ let region_a_param = ty::Region::new_early_bound(
+ tcx,
+ ty::EarlyBoundRegion {
+ def_id: region_a_param.def_id,
+ index: region_a_param.index,
+ name: region_a_param.name,
+ },
+ );
// Same for the region.
let region_b_param = gat_generics.param_at(*region_b_idx, tcx);
- let region_b_param = tcx.mk_re_early_bound(ty::EarlyBoundRegion {
- def_id: region_b_param.def_id,
- index: region_b_param.index,
- name: region_b_param.name,
- });
+ let region_b_param = ty::Region::new_early_bound(
+ tcx,
+ ty::EarlyBoundRegion {
+ def_id: region_b_param.def_id,
+ index: region_b_param.index,
+ name: region_b_param.name,
+ },
+ );
// The predicate we expect to see.
- let clause = ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
- ty::OutlivesPredicate(region_a_param, region_b_param),
- ));
- let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
- bounds.insert(clause);
+ bounds.insert(
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
+ region_a_param,
+ region_b_param,
+ ))
+ .to_predicate(tcx),
+ );
}
}
}
@@ -820,83 +839,20 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
let ty = tcx.type_of(param.def_id).subst_identity();
if tcx.features().adt_const_params {
- if let Some(non_structural_match_ty) =
- traits::search_for_adt_const_param_violation(param.span, tcx, ty)
- {
- // We use the same error code in both branches, because this is really the same
- // issue: we just special-case the message for type parameters to make it
- // clearer.
- match non_structural_match_ty.kind() {
- ty::Param(_) => {
- // Const parameters may not have type parameters as their types,
- // because we cannot be sure that the type parameter derives `PartialEq`
- // and `Eq` (just implementing them is not enough for `structural_match`).
- struct_span_err!(
- tcx.sess,
- hir_ty.span,
- E0741,
- "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \
- used as the type of a const parameter",
- )
- .span_label(
- hir_ty.span,
- format!("`{ty}` may not derive both `PartialEq` and `Eq`"),
- )
- .note(
- "it is not currently possible to use a type parameter as the type of a \
- const parameter",
- )
- .emit();
- }
- ty::Float(_) => {
- struct_span_err!(
- tcx.sess,
- hir_ty.span,
- E0741,
- "`{ty}` is forbidden as the type of a const generic parameter",
- )
- .note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
- .emit();
- }
- ty::FnPtr(_) => {
- struct_span_err!(
- tcx.sess,
- hir_ty.span,
- E0741,
- "using function pointers as const generic parameters is forbidden",
- )
- .emit();
- }
- ty::RawPtr(_) => {
- struct_span_err!(
- tcx.sess,
- hir_ty.span,
- E0741,
- "using raw pointers as const generic parameters is forbidden",
- )
- .emit();
- }
- _ => {
- let mut diag = struct_span_err!(
- tcx.sess,
- hir_ty.span,
- E0741,
- "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
- the type of a const parameter",
- non_structural_match_ty,
- );
-
- if ty == non_structural_match_ty {
- diag.span_label(
- hir_ty.span,
- format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
- );
- }
-
- diag.emit();
- }
- }
- }
+ enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
+ let trait_def_id =
+ tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span));
+ wfcx.register_bound(
+ ObligationCause::new(
+ hir_ty.span,
+ param.def_id,
+ ObligationCauseCode::ConstParam(ty),
+ ),
+ wfcx.param_env,
+ ty,
+ trait_def_id,
+ );
+ });
} else {
let err_ty_str;
let mut is_ptr = true;
@@ -1025,7 +981,7 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
// intermediate types must be sized.
let needs_drop_copy = || {
packed && {
- let ty = tcx.type_of(variant.fields.raw.last().unwrap().did).subst_identity();
+ let ty = tcx.type_of(variant.tail().did).subst_identity();
let ty = tcx.erase_regions(ty);
if ty.has_infer() {
tcx.sess
@@ -1078,9 +1034,9 @@ fn check_type_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, all_sized: b
tcx,
cause,
wfcx.param_env,
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(
ty::Const::from_anon_const(tcx, discr_def_id.expect_local()),
- )),
+ ))),
));
}
}
@@ -1133,7 +1089,7 @@ fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocIt
wfcx.infcx,
wfcx.param_env,
wfcx.body_def_id,
- normalized_bound,
+ normalized_bound.as_predicate(),
bound_span,
)
});
@@ -1154,20 +1110,32 @@ fn check_item_fn(
})
}
-fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_foreign_ty: bool) {
+enum UnsizedHandling {
+ Forbid,
+ Allow,
+ AllowIfForeignTail,
+}
+
+fn check_item_type(
+ tcx: TyCtxt<'_>,
+ item_id: LocalDefId,
+ ty_span: Span,
+ unsized_handling: UnsizedHandling,
+) {
debug!("check_item_type: {:?}", item_id);
enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| {
let ty = tcx.type_of(item_id).subst_identity();
let item_ty = wfcx.normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty);
- let mut forbid_unsized = true;
- if allow_foreign_ty {
- let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env);
- if let ty::Foreign(_) = tail.kind() {
- forbid_unsized = false;
+ let forbid_unsized = match unsized_handling {
+ UnsizedHandling::Forbid => true,
+ UnsizedHandling::Allow => false,
+ UnsizedHandling::AllowIfForeignTail => {
+ let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env);
+ !matches!(tail.kind(), ty::Foreign(_))
}
- }
+ };
wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into());
if forbid_unsized {
@@ -1398,7 +1366,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
}
let mut param_count = CountParams::default();
let has_region = pred.visit_with(&mut param_count).is_break();
- let substituted_pred = ty::EarlyBinder(pred).subst(tcx, substs);
+ let substituted_pred = ty::EarlyBinder::bind(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
@@ -1441,7 +1409,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
infcx,
wfcx.param_env.without_const(),
wfcx.body_def_id,
- p,
+ p.as_predicate(),
sp,
)
});
@@ -1503,7 +1471,7 @@ fn check_fn_or_method<'tcx>(
let span = tcx.def_span(def_id);
let has_implicit_self = hir_decl.implicit_self != hir::ImplicitSelfKind::None;
let mut inputs = sig.inputs().iter().skip(if has_implicit_self { 1 } else { 0 });
- // Check that the argument is a tuple
+ // Check that the argument is a tuple and is sized
if let Some(ty) = inputs.next() {
wfcx.register_bound(
ObligationCause::new(span, wfcx.body_def_id, ObligationCauseCode::RustCall),
@@ -1511,6 +1479,12 @@ fn check_fn_or_method<'tcx>(
*ty,
tcx.require_lang_item(hir::LangItem::Tuple, Some(span)),
);
+ wfcx.register_bound(
+ ObligationCause::new(span, wfcx.body_def_id, ObligationCauseCode::RustCall),
+ wfcx.param_env,
+ *ty,
+ tcx.require_lang_item(hir::LangItem::Sized, Some(span)),
+ );
} else {
tcx.sess.span_err(
hir_decl.inputs.last().map_or(span, |input| input.span),
@@ -1572,13 +1546,13 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
if let ty::Alias(ty::Opaque, unshifted_opaque_ty) = *ty.kind()
&& self.seen.insert(unshifted_opaque_ty.def_id)
&& let Some(opaque_def_id) = unshifted_opaque_ty.def_id.as_local()
- && let opaque = tcx.hir().expect_item(opaque_def_id).expect_opaque_ty()
- && let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = opaque.origin
+ && let origin = tcx.opaque_type_origin(opaque_def_id)
+ && let hir::OpaqueTyOrigin::FnReturn(source) | hir::OpaqueTyOrigin::AsyncFn(source) = origin
&& source == self.fn_def_id
{
let opaque_ty = tcx.fold_regions(unshifted_opaque_ty, |re, _depth| {
match re.kind() {
- ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReError(_) => re,
+ ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReError(_) | ty::ReStatic => re,
r => bug!("unexpected region: {r:?}"),
}
});
@@ -1591,7 +1565,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
self.wfcx.infcx,
self.wfcx.param_env,
self.wfcx.body_def_id,
- bound,
+ bound.as_predicate(),
bound_span,
));
// Set the debruijn index back to innermost here, since we already eagerly
@@ -1799,9 +1773,11 @@ fn check_variances_for_type_defn<'tcx>(
item: &hir::Item<'tcx>,
hir_generics: &hir::Generics<'_>,
) {
- let ty = tcx.type_of(item.owner_id).subst_identity();
- if tcx.has_error_field(ty) {
- return;
+ let identity_substs = ty::InternalSubsts::identity_for_item(tcx, item.owner_id);
+ for field in tcx.adt_def(item.owner_id).all_fields() {
+ if field.ty(tcx, identity_substs).references_error() {
+ return;
+ }
}
let ty_predicates = tcx.predicates_of(item.owner_id);
@@ -1902,7 +1878,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
// We lower empty bounds like `Vec<dyn Copy>:` as
// `WellFormed(Vec<dyn Copy>)`, which will later get checked by
// regular WF checking
- if let ty::PredicateKind::WellFormed(..) = pred.kind().skip_binder() {
+ if let ty::ClauseKind::WellFormed(..) = pred.kind().skip_binder() {
continue;
}
// Match the existing behavior.
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index a98d8e171..79cc43edf 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -336,15 +336,17 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) -> Coe
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
+ check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ref(tcx, r_b, ty))
}
(&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
- check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+ check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))
}
- (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)),
+ (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => {
+ check_mutbl(mt_a, mt_b, &|ty| Ty::new_imm_ptr(tcx, ty))
+ }
(&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
if def_a.is_struct() && def_b.is_struct() =>
@@ -571,7 +573,7 @@ fn infringing_fields_error(
.or_default()
.push(error.obligation.cause.span);
}
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref,
polarity: ty::ImplPolarity::Positive,
..
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index bd6252344..3bd293126 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -140,7 +140,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
impl1_def_id: DefId,
impl2_def_id: DefId,
) {
- traits::overlapping_impls(
+ let maybe_overlap = traits::overlapping_impls(
self.tcx,
impl1_def_id,
impl2_def_id,
@@ -148,11 +148,11 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
// inherent impls without warning.
SkipLeakCheck::Yes,
overlap_mode,
- )
- .map_or(true, |overlap| {
+ );
+
+ if let Some(overlap) = maybe_overlap {
self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
- false
- });
+ }
}
fn check_item(&mut self, id: hir::ItemId) {
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 4524b87a4..5097f4360 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -10,7 +10,6 @@ use rustc_errors::{error_code, struct_span_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
-use rustc_span::sym;
use rustc_trait_selection::traits;
mod builtin;
@@ -44,7 +43,7 @@ fn enforce_trait_manually_implementable(
let impl_header_span = tcx.def_span(impl_def_id);
// Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]`
- if tcx.has_attr(trait_def_id, sym::rustc_deny_explicit_impl) {
+ if tcx.trait_def(trait_def_id).deny_explicit_impl {
let trait_name = tcx.item_name(trait_def_id);
let mut err = struct_span_err!(
tcx.sess,
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 23beacd2a..025bab140 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -200,35 +200,32 @@ fn do_orphan_check_impl<'tcx>(
NonlocalImpl::DisallowOther,
),
- // trait Id { type This: ?Sized; }
- // impl<T: ?Sized> Id for T {
- // type This = T;
- // }
- // impl<T: ?Sized> AutoTrait for <T as Id>::This {}
- ty::Alias(AliasKind::Projection, _) => (
- LocalImpl::Disallow { problematic_kind: "associated type" },
- NonlocalImpl::DisallowOther,
- ),
-
- // ```
- // struct S<T>(T);
- // impl<T: ?Sized> S<T> {
- // type This = T;
- // }
- // impl<T: ?Sized> AutoTrait for S<T>::This {}
- // ```
- // FIXME(inherent_associated_types): The example code above currently leads to a cycle
- ty::Alias(AliasKind::Inherent, _) => (
- LocalImpl::Disallow { problematic_kind: "associated type" },
- NonlocalImpl::DisallowOther,
- ),
-
- // type Opaque = impl Trait;
- // impl AutoTrait for Opaque {}
- ty::Alias(AliasKind::Opaque, _) => (
- LocalImpl::Disallow { problematic_kind: "opaque type" },
- NonlocalImpl::DisallowOther,
- ),
+ ty::Alias(kind, _) => {
+ let problematic_kind = match kind {
+ // trait Id { type This: ?Sized; }
+ // impl<T: ?Sized> Id for T {
+ // type This = T;
+ // }
+ // impl<T: ?Sized> AutoTrait for <T as Id>::This {}
+ AliasKind::Projection => "associated type",
+ // type Foo = (impl Sized, bool)
+ // impl AutoTrait for Foo {}
+ AliasKind::Weak => "type alias",
+ // type Opaque = impl Trait;
+ // impl AutoTrait for Opaque {}
+ AliasKind::Opaque => "opaque type",
+ // ```
+ // struct S<T>(T);
+ // impl<T: ?Sized> S<T> {
+ // type This = T;
+ // }
+ // impl<T: ?Sized> AutoTrait for S<T>::This {}
+ // ```
+ // FIXME(inherent_associated_types): The example code above currently leads to a cycle
+ AliasKind::Inherent => "associated type",
+ };
+ (LocalImpl::Disallow { problematic_kind }, NonlocalImpl::DisallowOther)
+ }
ty::Bool
| ty::Char
@@ -346,7 +343,7 @@ fn emit_orphan_check_error<'tcx>(
// That way if we had `Vec<MyType>`, we will properly attribute the
// problem to `Vec<T>` and avoid confusing the user if they were to see
// `MyType` in the error.
- ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()),
+ ty::Adt(def, _) => Ty::new_adt(tcx, *def, ty::List::empty()),
_ => ty,
};
let msg = |ty: &str, postfix: &str| {
@@ -608,7 +605,9 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
}
let self_ty_root = match self_ty.kind() {
- ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())),
+ ty::Adt(def, _) => {
+ Ty::new_adt(tcx, *def, InternalSubsts::identity_for_item(tcx, def.did()))
+ }
_ => unimplemented!("unexpected self ty {:?}", self_ty),
};
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index ca0d5509c..f47df4f21 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -380,7 +380,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
}
fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
- self.tcx().ty_error_with_message(span, "bad placeholder type")
+ Ty::new_error_with_message(self.tcx(), span, "bad placeholder type")
}
fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
@@ -390,7 +390,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
// left alone.
r => bug!("unexpected region: {r:?}"),
});
- self.tcx().const_error_with_message(ty, span, "bad placeholder constant")
+ ty::Const::new_error_with_message(self.tcx(), ty, span, "bad placeholder constant")
}
fn projected_ty_from_poly_trait_ref(
@@ -407,7 +407,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
item_segment,
trait_ref.substs,
);
- self.tcx().mk_projection(item_def_id, item_substs)
+ Ty::new_projection(self.tcx(), item_def_id, item_substs)
} else {
// There are no late-bound regions; we can just ignore the binder.
let (mut mpart_sugg, mut inferred_sugg) = (None, None);
@@ -440,7 +440,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
self.tcx.replace_late_bound_regions_uncached(
poly_trait_ref,
|_| {
- self.tcx.mk_re_early_bound(ty::EarlyBoundRegion {
+ ty::Region::new_early_bound(self.tcx, ty::EarlyBoundRegion {
def_id: item_def_id,
index: 0,
name: Symbol::intern(&lt_name),
@@ -471,14 +471,15 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
}
_ => {}
}
- self.tcx().ty_error(self.tcx().sess.emit_err(
- errors::AssociatedTypeTraitUninferredGenericParams {
+ Ty::new_error(
+ self.tcx(),
+ self.tcx().sess.emit_err(errors::AssociatedTypeTraitUninferredGenericParams {
span,
inferred_sugg,
bound,
mpart_sugg,
- },
- ))
+ }),
+ )
}
}
@@ -666,17 +667,15 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
tcx.ensure().fn_sig(def_id);
}
- hir::TraitItemKind::Const(.., Some(_)) => {
- tcx.ensure().type_of(def_id);
- }
-
- hir::TraitItemKind::Const(hir_ty, _) => {
+ hir::TraitItemKind::Const(ty, body_id) => {
tcx.ensure().type_of(def_id);
- // Account for `const C: _;`.
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_trait_item(trait_item);
- if !tcx.sess.diagnostic().has_stashed_diagnostic(hir_ty.span, StashKey::ItemNoType) {
- placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
+ if !tcx.sess.diagnostic().has_stashed_diagnostic(ty.span, StashKey::ItemNoType)
+ && !(is_suggestable_infer_ty(ty) && body_id.is_some())
+ {
+ // Account for `const C: _;`.
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_trait_item(trait_item);
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
}
}
@@ -721,7 +720,14 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
}
- hir::ImplItemKind::Const(..) => {}
+ hir::ImplItemKind::Const(ty, _) => {
+ // Account for `const T: _ = ..;`
+ if !is_suggestable_infer_ty(ty) {
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_impl_item(impl_item);
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
+ }
+ }
}
}
@@ -941,7 +947,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
match item {
Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
- if !tcx.impl_defaultness(item.id.owner_id).has_value() {
+ if !tcx.defaultness(item.id.owner_id).has_value() {
tcx.sess.emit_err(errors::FunctionNotHaveDefaultImplementation {
span: item.span,
note_span: attr_span,
@@ -986,6 +992,50 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
no_dups.then_some(list)
});
+ let mut deny_explicit_impl = false;
+ let mut implement_via_object = true;
+ if let Some(attr) = tcx.get_attr(def_id, sym::rustc_deny_explicit_impl) {
+ deny_explicit_impl = true;
+ let mut seen_attr = false;
+ for meta in attr.meta_item_list().iter().flatten() {
+ if let Some(meta) = meta.meta_item()
+ && meta.name_or_empty() == sym::implement_via_object
+ && let Some(lit) = meta.name_value_literal()
+ {
+ if seen_attr {
+ tcx.sess.span_err(
+ meta.span,
+ "duplicated `implement_via_object` meta item",
+ );
+ }
+ seen_attr = true;
+
+ match lit.symbol {
+ kw::True => {
+ implement_via_object = true;
+ }
+ kw::False => {
+ implement_via_object = false;
+ }
+ _ => {
+ tcx.sess.span_err(
+ meta.span,
+ format!("unknown literal passed to `implement_via_object` attribute: {}", lit.symbol),
+ );
+ }
+ }
+ } else {
+ tcx.sess.span_err(
+ meta.span(),
+ format!("unknown meta item passed to `rustc_deny_explicit_impl` {:?}", meta),
+ );
+ }
+ }
+ if !seen_attr {
+ tcx.sess.span_err(attr.span, "missing `implement_via_object` meta item");
+ }
+ }
+
ty::TraitDef {
def_id: def_id.to_def_id(),
unsafety,
@@ -996,6 +1046,8 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
skip_array_during_method_dispatch,
specialization_kind,
must_implement_one_of,
+ implement_via_object,
+ deny_explicit_impl,
}
}
@@ -1124,7 +1176,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<ty::PolyFnSig<
bug!("unexpected sort of node in fn_sig(): {:?}", x);
}
};
- ty::EarlyBinder(output)
+ ty::EarlyBinder::bind(output)
}
fn infer_return_ty_for_fn_sig<'tcx>(
@@ -1188,7 +1240,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
} else {
ty::Binder::dummy(tcx.mk_fn_sig(
fn_sig.inputs().iter().copied(),
- tcx.ty_error(guar),
+ Ty::new_error(tcx, guar),
fn_sig.c_variadic,
fn_sig.unsafety,
fn_sig.abi,
@@ -1277,11 +1329,11 @@ fn suggest_impl_trait<'tcx>(
{
continue;
}
- let ocx = ObligationCtxt::new_in_snapshot(&infcx);
+ let ocx = ObligationCtxt::new(&infcx);
let item_ty = ocx.normalize(
&ObligationCause::misc(span, def_id),
param_env,
- tcx.mk_projection(assoc_item_def_id, substs),
+ Ty::new_projection(tcx, assoc_item_def_id, substs),
);
// FIXME(compiler-errors): We may benefit from resolving regions here.
if ocx.select_where_possible().is_empty()
@@ -1312,7 +1364,7 @@ fn impl_trait_ref(
check_impl_constness(tcx, impl_.constness, ast_trait_ref),
)
})
- .map(ty::EarlyBinder)
+ .map(ty::EarlyBinder::bind)
}
fn check_impl_constness(
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index ed60998ec..ccc9f8084 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -9,7 +9,7 @@ use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint;
use rustc_span::symbol::{kw, Symbol};
-use rustc_span::Span;
+use rustc_span::{sym, Span};
pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
use rustc_hir::*;
@@ -50,7 +50,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
// We do not allow generic parameters in anon consts if we are inside
// of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
None
- } else if tcx.lazy_normalization() {
+ } else if tcx.features().generic_const_exprs {
let parent_node = tcx.hir().get_parent(hir_id);
if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node
&& constant.hir_id == hir_id
@@ -101,6 +101,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
param_def_id_to_index,
has_self: generics.has_self,
has_late_bound_regions: generics.has_late_bound_regions,
+ host_effect_index: None,
};
} else {
// HACK(eddyb) this provides the correct generics when
@@ -123,9 +124,6 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
{
Some(parent_def_id.to_def_id())
}
- Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
- Some(tcx.typeck_root_def_id(def_id.to_def_id()))
- }
// Exclude `GlobalAsm` here which cannot have generics.
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
@@ -142,11 +140,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
}
}
- Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
+ Node::ConstBlock(_)
+ | Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
Some(tcx.typeck_root_def_id(def_id.to_def_id()))
}
Node::Item(item) => match item.kind {
- ItemKind::OpaqueTy(hir::OpaqueTy {
+ ItemKind::OpaqueTy(&hir::OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
in_trait,
@@ -228,10 +227,12 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
let has_self = opt_self.is_some();
let mut parent_has_self = false;
let mut own_start = has_self as u32;
+ let mut host_effect_index = None;
let parent_count = parent_def_id.map_or(0, |def_id| {
let generics = tcx.generics_of(def_id);
assert!(!has_self);
parent_has_self = generics.has_self;
+ host_effect_index = generics.host_effect_index;
own_start = generics.count() as u32;
generics.parent_count + generics.params.len()
});
@@ -253,11 +254,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
// Now create the real type and const parameters.
let type_start = own_start - has_self as u32 + params.len() as u32;
- let mut i = 0;
+ let mut i: u32 = 0;
let mut next_index = || {
let prev = i;
i += 1;
- prev as u32 + type_start
+ prev + type_start
};
const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
@@ -297,7 +298,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
})
}
GenericParamKind::Const { default, .. } => {
- if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
+ let is_host_param = tcx.has_attr(param.def_id, sym::rustc_host);
+
+ if !matches!(allow_defaults, Defaults::Allowed)
+ && default.is_some()
+ // `rustc_host` effect params are allowed to have defaults.
+ && !is_host_param
+ {
tcx.sess.span_err(
param.span,
"defaults for const parameters are only allowed in \
@@ -305,8 +312,18 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
);
}
+ let index = next_index();
+
+ if is_host_param {
+ if let Some(idx) = host_effect_index {
+ bug!("parent also has host effect param? index: {idx}, def: {def_id:?}");
+ }
+
+ host_effect_index = Some(parent_count + index as usize);
+ }
+
Some(ty::GenericParamDef {
- index: next_index(),
+ index,
name: param.name.ident().name,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
@@ -339,17 +356,14 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
// provide junk type parameter defs for const blocks.
- if let Node::AnonConst(_) = node {
- let parent_node = tcx.hir().get_parent(hir_id);
- if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
- params.push(ty::GenericParamDef {
- index: next_index(),
- name: Symbol::intern("<const_ty>"),
- def_id: def_id.to_def_id(),
- pure_wrt_drop: false,
- kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
- });
- }
+ if let Node::ConstBlock(_) = node {
+ params.push(ty::GenericParamDef {
+ index: next_index(),
+ name: Symbol::intern("<const_ty>"),
+ def_id: def_id.to_def_id(),
+ pure_wrt_drop: false,
+ kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
+ });
}
let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect();
@@ -361,6 +375,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
param_def_id_to_index,
has_self: has_self || parent_has_self,
has_late_bound_regions: has_late_bound_regions(tcx, node),
+ host_effect_index,
}
}
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 948b903e5..57f74172e 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -1,5 +1,5 @@
use super::ItemCtxt;
-use crate::astconv::{AstConv, OnlySelfBounds};
+use crate::astconv::{AstConv, PredicateFilter};
use rustc_hir as hir;
use rustc_infer::traits::util;
use rustc_middle::ty::subst::InternalSubsts;
@@ -19,32 +19,34 @@ fn associated_type_bounds<'tcx>(
assoc_item_def_id: LocalDefId,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
span: Span,
-) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
- let item_ty = tcx.mk_projection(
+) -> &'tcx [(ty::Clause<'tcx>, Span)] {
+ let item_ty = Ty::new_projection(
+ tcx,
assoc_item_def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, assoc_item_def_id),
);
let icx = ItemCtxt::new(tcx, assoc_item_def_id);
- let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, OnlySelfBounds(false));
+ let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All);
// Associated types are implicitly sized unless a `?Sized` bound is found
icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span);
let trait_def_id = tcx.local_parent(assoc_item_def_id);
let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id);
- let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| {
- match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(tr)) => tr.self_ty() == item_ty,
- ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => {
- proj.projection_ty.self_ty() == item_ty
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(outlives)) => outlives.0 == item_ty,
+ let bounds_from_parent = trait_predicates
+ .predicates
+ .iter()
+ .copied()
+ .filter(|(pred, _)| match pred.kind().skip_binder() {
+ ty::ClauseKind::Trait(tr) => tr.self_ty() == item_ty,
+ ty::ClauseKind::Projection(proj) => proj.projection_ty.self_ty() == item_ty,
+ ty::ClauseKind::TypeOutlives(outlives) => outlives.0 == item_ty,
_ => false,
- }
- });
+ })
+ .map(|(clause, span)| (clause, span));
- let all_bounds = tcx.arena.alloc_from_iter(bounds.predicates().chain(bounds_from_parent));
+ let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent));
debug!(
"associated_type_bounds({}) = {:?}",
tcx.def_path_str(assoc_item_def_id.to_def_id()),
@@ -64,33 +66,34 @@ fn opaque_type_bounds<'tcx>(
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
item_ty: Ty<'tcx>,
span: Span,
-) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
+) -> &'tcx [(ty::Clause<'tcx>, Span)] {
ty::print::with_no_queries!({
let icx = ItemCtxt::new(tcx, opaque_def_id);
- let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, OnlySelfBounds(false));
+ let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds, PredicateFilter::All);
// Opaque types are implicitly sized unless a `?Sized` bound is found
icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span);
debug!(?bounds);
- tcx.arena.alloc_from_iter(bounds.predicates())
+ tcx.arena.alloc_from_iter(bounds.clauses())
})
}
pub(super) fn explicit_item_bounds(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
-) -> ty::EarlyBinder<&'_ [(ty::Predicate<'_>, Span)]> {
+) -> ty::EarlyBinder<&'_ [(ty::Clause<'_>, Span)]> {
match tcx.opt_rpitit_info(def_id.to_def_id()) {
// RPITIT's bounds are the same as opaque type bounds, but with
// a projection self type.
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
let item = tcx.hir().get_by_def_id(opaque_def_id.expect_local()).expect_item();
let opaque_ty = item.expect_opaque_ty();
- return ty::EarlyBinder(opaque_type_bounds(
+ return ty::EarlyBinder::bind(opaque_type_bounds(
tcx,
opaque_def_id.expect_local(),
opaque_ty.bounds,
- tcx.mk_projection(
+ Ty::new_projection(
+ tcx,
def_id.to_def_id(),
ty::InternalSubsts::identity_for_item(tcx, def_id),
),
@@ -116,25 +119,23 @@ pub(super) fn explicit_item_bounds(
}) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
let item_ty = if *in_trait && !tcx.lower_impl_trait_in_trait_to_assoc_ty() {
- tcx.mk_projection(def_id.to_def_id(), substs)
+ Ty::new_projection(tcx, def_id.to_def_id(), substs)
} else {
- tcx.mk_opaque(def_id.to_def_id(), substs)
+ Ty::new_opaque(tcx, def_id.to_def_id(), substs)
};
opaque_type_bounds(tcx, def_id, bounds, item_ty, *span)
}
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::TyAlias(..), .. }) => &[],
_ => bug!("item_bounds called on {:?}", def_id),
};
- ty::EarlyBinder(bounds)
+ ty::EarlyBinder::bind(bounds)
}
pub(super) fn item_bounds(
tcx: TyCtxt<'_>,
def_id: DefId,
-) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
+) -> ty::EarlyBinder<&'_ ty::List<ty::Clause<'_>>> {
tcx.explicit_item_bounds(def_id).map_bound(|bounds| {
- tcx.mk_predicates_from_iter(util::elaborate(
- tcx,
- bounds.iter().map(|&(bound, _span)| bound),
- ))
+ tcx.mk_clauses_from_iter(util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)))
})
}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index e5b5dae55..129366641 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -1,8 +1,8 @@
-use crate::astconv::{AstConv, OnlySelfBounds};
+use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
use crate::bounds::Bounds;
use crate::collect::ItemCtxt;
use crate::constrained_generic_params as cgp;
-use hir::{HirId, Node};
+use hir::{HirId, Lifetime, Node};
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
@@ -10,9 +10,9 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{GenericPredicates, ToPredicate};
+use rustc_middle::ty::{GenericPredicates, Generics, ImplTraitInTraitData, ToPredicate};
use rustc_span::symbol::{sym, Ident};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{Span, Symbol, DUMMY_SP};
/// Returns a list of all type predicates (explicit and implicit) for the definition with
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@@ -62,6 +62,67 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> {
use rustc_hir::*;
+ match tcx.opt_rpitit_info(def_id.to_def_id()) {
+ Some(ImplTraitInTraitData::Trait { opaque_def_id, fn_def_id }) => {
+ let opaque_ty_id = tcx.hir().local_def_id_to_hir_id(opaque_def_id.expect_local());
+ let opaque_ty_node = tcx.hir().get(opaque_ty_id);
+ let Node::Item(&Item { kind: ItemKind::OpaqueTy(OpaqueTy { lifetime_mapping, .. }), .. }) = opaque_ty_node else {
+ bug!("unexpected {opaque_ty_node:?}")
+ };
+
+ let mut predicates = Vec::new();
+
+ // RPITITs should inherit the predicates of their parent. This is
+ // both to ensure that the RPITITs are only instantiated when the
+ // parent predicates would hold, and also so that the param-env
+ // inherits these predicates as assumptions.
+ let identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
+ predicates.extend(
+ tcx.explicit_predicates_of(fn_def_id).instantiate_own(tcx, identity_substs),
+ );
+
+ // We also install bidirectional outlives predicates for the RPITIT
+ // to keep the duplicates lifetimes from opaque lowering in sync.
+ compute_bidirectional_outlives_predicates(
+ tcx,
+ def_id,
+ lifetime_mapping.iter().map(|(lifetime, def_id)| {
+ (*lifetime, (*def_id, lifetime.ident.name, lifetime.ident.span))
+ }),
+ tcx.generics_of(def_id.to_def_id()),
+ &mut predicates,
+ );
+
+ return ty::GenericPredicates {
+ parent: Some(tcx.parent(def_id.to_def_id())),
+ predicates: tcx.arena.alloc_from_iter(predicates),
+ };
+ }
+
+ Some(ImplTraitInTraitData::Impl { fn_def_id }) => {
+ let assoc_item = tcx.associated_item(def_id);
+ let trait_assoc_predicates =
+ tcx.explicit_predicates_of(assoc_item.trait_item_def_id.unwrap());
+
+ let impl_assoc_identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
+ let impl_def_id = tcx.parent(fn_def_id);
+ let impl_trait_ref_substs =
+ tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity().substs;
+
+ let impl_assoc_substs =
+ impl_assoc_identity_substs.rebase_onto(tcx, impl_def_id, impl_trait_ref_substs);
+
+ let impl_predicates = trait_assoc_predicates.instantiate_own(tcx, impl_assoc_substs);
+
+ return ty::GenericPredicates {
+ parent: Some(impl_def_id),
+ predicates: tcx.arena.alloc_from_iter(impl_predicates),
+ };
+ }
+
+ None => {}
+ }
+
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let node = tcx.hir().get(hir_id);
@@ -75,7 +136,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
// We use an `IndexSet` to preserve order of insertion.
// Preserving the order of insertion is important here so as not to break UI tests.
- let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
+ let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default();
let ast_generics = match node {
Node::TraitItem(item) => item.generics,
@@ -125,8 +186,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
if let Some(self_bounds) = is_trait {
predicates.extend(
icx.astconv()
- .compute_bounds(tcx.types.self_param, self_bounds, OnlySelfBounds(false))
- .predicates(),
+ .compute_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All)
+ .clauses(),
);
}
@@ -175,22 +236,24 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
param.span,
);
trace!(?bounds);
- predicates.extend(bounds.predicates());
+ predicates.extend(bounds.clauses());
trace!(?predicates);
}
GenericParamKind::Const { .. } => {
let name = param.name.ident().name;
let param_const = ty::ParamConst::new(index, name);
- let ct_ty = tcx.type_of(param.def_id.to_def_id()).subst_identity();
+ let ct_ty = tcx
+ .type_of(param.def_id.to_def_id())
+ .no_bound_vars()
+ .expect("const parameters cannot be generic");
- let ct = tcx.mk_const(param_const, ct_ty);
+ let ct = ty::Const::new_param(tcx, param_const, ct_ty);
- let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
- ty::Clause::ConstArgHasType(ct, ct_ty),
- ))
- .to_predicate(tcx);
- predicates.insert((predicate, param.span));
+ predicates.insert((
+ ty::ClauseKind::ConstArgHasType(ct, ct_ty).to_predicate(tcx),
+ param.span,
+ ));
index += 1;
}
@@ -219,7 +282,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
} else {
let span = bound_pred.bounded_ty.span;
let predicate = ty::Binder::bind_with_vars(
- ty::PredicateKind::WellFormed(ty.into()),
+ ty::ClauseKind::WellFormed(ty.into()),
bound_vars,
);
predicates.insert((predicate.to_predicate(tcx), span));
@@ -234,7 +297,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
bound_vars,
OnlySelfBounds(false),
);
- predicates.extend(bounds.predicates());
+ predicates.extend(bounds.clauses());
}
hir::WherePredicate::RegionPredicate(region_pred) => {
@@ -246,11 +309,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
_ => bug!(),
};
- let pred = ty::Binder::dummy(ty::PredicateKind::Clause(
- ty::Clause::RegionOutlives(ty::OutlivesPredicate(r1, r2)),
- ))
- .to_predicate(icx.tcx);
-
+ let pred = ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2))
+ .to_predicate(icx.tcx);
(pred, span)
}))
}
@@ -293,39 +353,22 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
bug!("unexpected {opaque_ty_node:?}")
};
debug!(?lifetimes);
- for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) {
- let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
- let orig_region = icx.astconv().ast_region_to_region(&arg, None);
- if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
- // Only early-bound regions can point to the original generic parameter.
- continue;
- }
- let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue };
- let dup_def = duplicate.def_id.to_def_id();
-
- let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() };
-
- let dup_region = tcx.mk_re_early_bound(ty::EarlyBoundRegion {
- def_id: dup_def,
- index: dup_index,
- name: duplicate.name.ident().name,
- });
- predicates.push((
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
- ty::OutlivesPredicate(orig_region, dup_region),
- )))
- .to_predicate(icx.tcx),
- duplicate.span,
- ));
- predicates.push((
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
- ty::OutlivesPredicate(dup_region, orig_region),
- )))
- .to_predicate(icx.tcx),
- duplicate.span,
- ));
- }
+ let lifetime_mapping = std::iter::zip(lifetimes, ast_generics.params)
+ .map(|(arg, dup)| {
+ let hir::GenericArg::Lifetime(arg) = arg else { bug!() };
+ (**arg, dup)
+ })
+ .filter(|(_, dup)| matches!(dup.kind, hir::GenericParamKind::Lifetime { .. }))
+ .map(|(lifetime, dup)| (lifetime, (dup.def_id, dup.name.ident().name, dup.span)));
+
+ compute_bidirectional_outlives_predicates(
+ tcx,
+ def_id,
+ lifetime_mapping,
+ generics,
+ &mut predicates,
+ );
debug!(?predicates);
}
@@ -335,13 +378,53 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
}
}
+/// Opaques have duplicated lifetimes and we need to compute bidirectional outlives predicates to
+/// enforce that these lifetimes stay in sync.
+fn compute_bidirectional_outlives_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ item_def_id: LocalDefId,
+ lifetime_mapping: impl Iterator<Item = (Lifetime, (LocalDefId, Symbol, Span))>,
+ generics: &Generics,
+ predicates: &mut Vec<(ty::Clause<'tcx>, Span)>,
+) {
+ let icx = ItemCtxt::new(tcx, item_def_id);
+
+ for (arg, (dup_def, name, span)) in lifetime_mapping {
+ let orig_region = icx.astconv().ast_region_to_region(&arg, None);
+ if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) {
+ // There is no late-bound lifetime to actually match up here, since the lifetime doesn't
+ // show up in the opaque's parent's substs.
+ continue;
+ }
+
+ let Some(dup_index) = generics.param_def_id_to_index(icx.tcx, dup_def.to_def_id()) else { bug!() };
+
+ let dup_region = ty::Region::new_early_bound(
+ tcx,
+ ty::EarlyBoundRegion { def_id: dup_def.to_def_id(), index: dup_index, name },
+ );
+
+ predicates.push((
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(orig_region, dup_region))
+ .to_predicate(tcx),
+ span,
+ ));
+
+ predicates.push((
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(dup_region, orig_region))
+ .to_predicate(tcx),
+ span,
+ ));
+ }
+}
+
fn const_evaluatable_predicates_of(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
-) -> FxIndexSet<(ty::Predicate<'_>, Span)> {
+) -> FxIndexSet<(ty::Clause<'_>, Span)> {
struct ConstCollector<'tcx> {
tcx: TyCtxt<'tcx>,
- preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
+ preds: FxIndexSet<(ty::Clause<'tcx>, Span)>,
}
impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
@@ -349,11 +432,8 @@ fn const_evaluatable_predicates_of(
let ct = ty::Const::from_anon_const(self.tcx, c.def_id);
if let ty::ConstKind::Unevaluated(_) = ct.kind() {
let span = self.tcx.def_span(c.def_id);
- self.preds.insert((
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct))
- .to_predicate(self.tcx),
- span,
- ));
+ self.preds
+ .insert((ty::ClauseKind::ConstEvaluatable(ct).to_predicate(self.tcx), span));
}
}
@@ -441,13 +521,9 @@ pub(super) fn explicit_predicates_of<'tcx>(
.iter()
.copied()
.filter(|(pred, _)| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(tr)) => !is_assoc_item_ty(tr.self_ty()),
- ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => {
- !is_assoc_item_ty(proj.projection_ty.self_ty())
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(outlives)) => {
- !is_assoc_item_ty(outlives.0)
- }
+ ty::ClauseKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()),
+ ty::ClauseKind::Projection(proj) => !is_assoc_item_ty(proj.projection_ty.self_ty()),
+ ty::ClauseKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0),
_ => true,
})
.collect();
@@ -460,7 +536,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
}
}
} else {
- if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
+ if matches!(def_kind, DefKind::AnonConst) && tcx.features().generic_const_exprs {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let parent_def_id = tcx.hir().get_parent_item(hir_id);
@@ -488,9 +564,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
.predicates
.into_iter()
.filter(|(pred, _)| {
- if let ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, _)) =
- pred.kind().skip_binder()
- {
+ if let ty::ClauseKind::ConstArgHasType(ct, _) = pred.kind().skip_binder() {
match ct.kind() {
ty::ConstKind::Param(param_const) => {
let defaulted_param_idx = tcx
@@ -544,19 +618,6 @@ pub(super) fn explicit_predicates_of<'tcx>(
}
}
-#[derive(Copy, Clone, Debug)]
-pub enum PredicateFilter {
- /// All predicates may be implied by the trait
- All,
-
- /// Only traits that reference `Self: ..` are implied by the trait
- SelfOnly,
-
- /// Only traits that reference `Self: ..` and define an associated type
- /// with the given ident are implied by the trait
- SelfThatDefines(Ident),
-}
-
/// Ensures that the super-predicates of the trait with a `DefId`
/// of `trait_def_id` are converted and stored. This also ensures that
/// the transitive super-predicates are converted.
@@ -578,11 +639,15 @@ pub(super) fn implied_predicates_of(
tcx: TyCtxt<'_>,
trait_def_id: LocalDefId,
) -> ty::GenericPredicates<'_> {
- if tcx.is_trait_alias(trait_def_id.to_def_id()) {
- implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::All)
- } else {
- tcx.super_predicates_of(trait_def_id)
- }
+ implied_predicates_with_filter(
+ tcx,
+ trait_def_id.to_def_id(),
+ if tcx.is_trait_alias(trait_def_id.to_def_id()) {
+ PredicateFilter::All
+ } else {
+ PredicateFilter::SelfAndAssociatedTypeBounds
+ },
+ )
}
/// Ensures that the super-predicates of the trait with a `DefId`
@@ -615,48 +680,18 @@ pub(super) fn implied_predicates_with_filter(
let icx = ItemCtxt::new(tcx, trait_def_id);
let self_param_ty = tcx.types.self_param;
- let (superbounds, where_bounds_that_match) = match filter {
- PredicateFilter::All => (
- // Convert the bounds that follow the colon (or equal in trait aliases)
- icx.astconv().compute_bounds(self_param_ty, bounds, OnlySelfBounds(false)),
- // Also include all where clause bounds
- icx.type_parameter_bounds_in_generics(
- generics,
- item.owner_id.def_id,
- self_param_ty,
- OnlySelfBounds(false),
- None,
- ),
- ),
- PredicateFilter::SelfOnly => (
- // Convert the bounds that follow the colon (or equal in trait aliases)
- icx.astconv().compute_bounds(self_param_ty, bounds, OnlySelfBounds(true)),
- // Include where clause bounds for `Self`
- icx.type_parameter_bounds_in_generics(
- generics,
- item.owner_id.def_id,
- self_param_ty,
- OnlySelfBounds(true),
- None,
- ),
- ),
- PredicateFilter::SelfThatDefines(assoc_name) => (
- // Convert the bounds that follow the colon (or equal) that reference the associated name
- icx.astconv().compute_bounds_that_match_assoc_item(self_param_ty, bounds, assoc_name),
- // Include where clause bounds for `Self` that reference the associated name
- icx.type_parameter_bounds_in_generics(
- generics,
- item.owner_id.def_id,
- self_param_ty,
- OnlySelfBounds(true),
- Some(assoc_name),
- ),
- ),
- };
+ let superbounds = icx.astconv().compute_bounds(self_param_ty, bounds, filter);
+
+ let where_bounds_that_match = icx.type_parameter_bounds_in_generics(
+ generics,
+ item.owner_id.def_id,
+ self_param_ty,
+ filter,
+ );
// Combine the two lists to form the complete set of superbounds:
let implied_bounds =
- &*tcx.arena.alloc_from_iter(superbounds.predicates().chain(where_bounds_that_match));
+ &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match));
debug!(?implied_bounds);
// Now require that immediate supertraits are converted, which will, in
@@ -665,7 +700,7 @@ pub(super) fn implied_predicates_with_filter(
if matches!(filter, PredicateFilter::SelfOnly) {
for &(pred, span) in implied_bounds {
debug!("superbound: {:?}", pred);
- if let ty::PredicateKind::Clause(ty::Clause::Trait(bound)) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder()
&& bound.polarity == ty::ImplPolarity::Positive
{
tcx.at(span).super_predicates_of(bound.def_id());
@@ -684,6 +719,7 @@ pub(super) fn type_param_predicates(
(item_def_id, def_id, assoc_name): (LocalDefId, LocalDefId, Ident),
) -> ty::GenericPredicates<'_> {
use rustc_hir::*;
+ use rustc_middle::ty::Ty;
// In the AST, bounds can derive from two places. Either
// written inline like `<T: Foo>` or in a where-clause like
@@ -693,7 +729,7 @@ pub(super) fn type_param_predicates(
let param_owner = tcx.hir().ty_param_owner(def_id);
let generics = tcx.generics_of(param_owner);
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
- let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id));
+ let ty = Ty::new_param(tcx, index, tcx.hir().ty_param_name(def_id));
// Don't look for bounds where the type parameter isn't in scope.
let parent = if item_def_id == param_owner {
@@ -721,7 +757,7 @@ pub(super) fn type_param_predicates(
ItemKind::Fn(.., generics, _)
| ItemKind::Impl(&hir::Impl { generics, .. })
| ItemKind::TyAlias(_, generics)
- | ItemKind::OpaqueTy(OpaqueTy {
+ | ItemKind::OpaqueTy(&OpaqueTy {
generics,
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
@@ -757,12 +793,11 @@ pub(super) fn type_param_predicates(
ast_generics,
def_id,
ty,
- OnlySelfBounds(true),
- Some(assoc_name),
+ PredicateFilter::SelfThatDefines(assoc_name),
)
.into_iter()
.filter(|(predicate, _)| match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => data.self_ty().is_param(index),
+ ty::ClauseKind::Trait(data) => data.self_ty().is_param(index),
_ => false,
}),
);
@@ -782,9 +817,8 @@ impl<'tcx> ItemCtxt<'tcx> {
ast_generics: &'tcx hir::Generics<'tcx>,
param_def_id: LocalDefId,
ty: Ty<'tcx>,
- only_self_bounds: OnlySelfBounds,
- assoc_name: Option<Ident>,
- ) -> Vec<(ty::Predicate<'tcx>, Span)> {
+ filter: PredicateFilter,
+ ) -> Vec<(ty::Clause<'tcx>, Span)> {
let mut bounds = Bounds::default();
for predicate in ast_generics.predicates {
@@ -792,9 +826,23 @@ impl<'tcx> ItemCtxt<'tcx> {
continue;
};
+ let (only_self_bounds, assoc_name) = match filter {
+ PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {
+ (OnlySelfBounds(false), None)
+ }
+ PredicateFilter::SelfOnly => (OnlySelfBounds(true), None),
+ PredicateFilter::SelfThatDefines(assoc_name) => {
+ (OnlySelfBounds(true), Some(assoc_name))
+ }
+ };
+
+ // Subtle: If we're collecting `SelfAndAssociatedTypeBounds`, then we
+ // want to only consider predicates with `Self: ...`, but we don't want
+ // `OnlySelfBounds(true)` since we want to collect the nested associated
+ // type bound as well.
let bound_ty = if predicate.is_param_bound(param_def_id.to_def_id()) {
ty
- } else if !only_self_bounds.0 {
+ } else if matches!(filter, PredicateFilter::All) {
self.to_ty(predicate.bounded_ty)
} else {
continue;
@@ -813,7 +861,7 @@ impl<'tcx> ItemCtxt<'tcx> {
);
}
- bounds.predicates().collect()
+ bounds.clauses().collect()
}
#[instrument(level = "trace", skip(self))]
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 794812a5c..acd0bcd8e 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -22,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_session::lint;
use rustc_span::def_id::DefId;
use rustc_span::symbol::{sym, Ident};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use std::fmt;
use crate::errors;
@@ -338,7 +338,17 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::TraitRefBoundary { .. } => {
// We should only see super trait lifetimes if there is a `Binder` above
- assert!(supertrait_bound_vars.is_empty());
+ // though this may happen when we call `poly_trait_ref_binder_info` with
+ // an (erroneous, #113423) associated return type bound in an impl header.
+ if !supertrait_bound_vars.is_empty() {
+ self.tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!(
+ "found supertrait lifetimes without a binder to append \
+ them to: {supertrait_bound_vars:?}"
+ ),
+ );
+ }
break (vec![], BinderScopeType::Normal);
}
@@ -556,7 +566,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
});
}
}
- hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+ hir::ItemKind::OpaqueTy(&hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::FnReturn(parent) | hir::OpaqueTyOrigin::AsyncFn(parent),
generics,
..
@@ -1344,12 +1354,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Binder {
where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
} => {
- let mut err = self.tcx.sess.struct_span_err(
- lifetime_ref.ident.span,
- "`impl Trait` can only mention lifetimes bound at the fn or impl level",
- );
- err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
- err.emit();
+ self.tcx.sess.emit_err(errors::LateBoundInApit::Lifetime {
+ span: lifetime_ref.ident.span,
+ param_span: self.tcx.def_span(region_def_id),
+ });
return;
}
Scope::Root { .. } => break,
@@ -1379,6 +1387,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
let mut late_depth = 0;
let mut scope = self.scope;
let mut crossed_anon_const = false;
+
let result = loop {
match *scope {
Scope::Body { s, .. } => {
@@ -1446,6 +1455,50 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
return;
}
+ // We may fail to resolve higher-ranked ty/const vars that are mentioned by APIT.
+ // AST-based resolution does not care for impl-trait desugaring, which are the
+ // responsibility of lowering. This may create a mismatch between the resolution
+ // AST found (`param_def_id`) which points to HRTB, and what HIR allows.
+ // ```
+ // fn foo(x: impl for<T> Trait<Assoc = impl Trait2<T>>) {}
+ // ```
+ //
+ // In such case, walk back the binders to diagnose it properly.
+ let mut scope = self.scope;
+ loop {
+ match *scope {
+ Scope::Binder {
+ where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+ } => {
+ let guar = self.tcx.sess.emit_err(match self.tcx.def_kind(param_def_id) {
+ DefKind::TyParam => errors::LateBoundInApit::Type {
+ span: self.tcx.hir().span(hir_id),
+ param_span: self.tcx.def_span(param_def_id),
+ },
+ DefKind::ConstParam => errors::LateBoundInApit::Const {
+ span: self.tcx.hir().span(hir_id),
+ param_span: self.tcx.def_span(param_def_id),
+ },
+ kind => {
+ bug!("unexpected def-kind: {}", kind.descr(param_def_id.to_def_id()))
+ }
+ });
+ self.map.defs.insert(hir_id, ResolvedArg::Error(guar));
+ return;
+ }
+ Scope::Root { .. } => break,
+ Scope::Binder { s, .. }
+ | Scope::Body { s, .. }
+ | Scope::Elision { s, .. }
+ | Scope::ObjectLifetimeDefault { s, .. }
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. }
+ | Scope::AnonConstBoundary { s } => {
+ scope = s;
+ }
+ }
+ }
+
self.tcx.sess.delay_span_bug(
self.tcx.hir().span(hir_id),
format!("could not resolve {param_def_id:?}"),
@@ -1761,7 +1814,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::ClauseKind::Trait(data) => {
// The order here needs to match what we would get from `subst_supertrait`
let pred_bound_vars = bound_predicate.bound_vars();
let mut all_bound_vars = bound_vars.clone();
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 8e082d3c5..3755342ae 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -16,6 +16,7 @@ mod opaque;
fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
use hir::*;
+ use rustc_middle::ty::Ty;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let Node::AnonConst(_) = tcx.hir().get(hir_id) else { panic!() };
@@ -25,21 +26,15 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
let (generics, arg_idx) = match parent_node {
// Easy case: arrays repeat expressions.
- Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
+ Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
if constant.hir_id() == hir_id =>
{
return tcx.types.usize
}
- Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
+ Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
return tcx.typeck(def_id).node_type(e.hir_id)
}
- Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
- if anon_const.hir_id == hir_id =>
- {
- let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
- return substs.as_inline_const().ty()
- }
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
@@ -73,7 +68,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
) =>
{
let Some(trait_def_id) = trait_ref.trait_def_id() else {
- return tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find trait");
+ return Ty::new_error_with_message(tcx,tcx.def_span(def_id), "Could not find trait");
};
let assoc_items = tcx.associated_items(trait_def_id);
let assoc_item = assoc_items.find_by_name_and_kind(
@@ -85,7 +80,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
.expect("const parameter types cannot be generic")
} else {
// FIXME(associated_const_equality): add a useful error message here.
- tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find associated const on trait")
+ Ty::new_error_with_message(tcx,tcx.def_span(def_id), "Could not find associated const on trait")
}
}
@@ -105,7 +100,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
// arm would handle this.
//
// I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU
- Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
+ Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
// Find the Item containing the associated type so we can create an ItemCtxt.
// Using the ItemCtxt convert the HIR for the unresolved assoc type into a
// ty which is a fully resolved projection.
@@ -143,7 +138,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
(generics, arg_index)
} else {
// I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
"unexpected non-GAT usage of an anon const",
);
@@ -160,7 +155,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
// As there is no relevant param for `def_id`, we simply return
// `None` here.
let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
format!("unable to find type-dependent def for {:?}", parent_node_id),
);
@@ -180,12 +175,12 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
(tcx.generics_of(type_dependent_def), idx)
}
- Node::Ty(&Ty { kind: TyKind::Path(_), .. })
+ Node::Ty(&hir::Ty { kind: TyKind::Path(_), .. })
| Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. })
| Node::TraitRef(..)
| Node::Pat(_) => {
let path = match parent_node {
- Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
+ Node::Ty(&hir::Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
| Node::TraitRef(&TraitRef { path, .. }) => &*path,
Node::Expr(&Expr {
kind:
@@ -201,14 +196,14 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) {
path
} else {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
format!("unable to find const parent for {} in pat {:?}", hir_id, pat),
);
}
}
_ => {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
format!("unexpected const parent path {:?}", parent_node),
);
@@ -230,7 +225,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
.position(|ct| ct.hir_id == hir_id)
.map(|idx| (idx, seg)))
}) else {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
"no arg matching AnonConst in path",
);
@@ -239,7 +234,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
let generics = match tcx.res_generics_def_id(segment.res) {
Some(def_id) => tcx.generics_of(def_id),
None => {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
);
@@ -249,7 +244,7 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
(generics, arg_index)
}
- _ => return tcx.ty_error_with_message(
+ _ => return Ty::new_error_with_message(tcx,
tcx.def_span(def_id),
format!("unexpected const parent in type_of(): {parent_node:?}"),
),
@@ -275,7 +270,8 @@ fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
{
tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic")
} else {
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(
+ tcx,
tcx.def_span(def_id),
format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"),
);
@@ -311,6 +307,9 @@ fn get_path_containing_arg_in_pat<'hir>(
}
pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty<'_>> {
+ use rustc_hir::*;
+ use rustc_middle::ty::Ty;
+
// If we are computing `type_of` the synthesized associated type for an RPITIT in the impl
// side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the
// associated type in the impl.
@@ -323,7 +322,8 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
return map[&assoc_item.trait_item_def_id.unwrap()];
}
Err(_) => {
- return ty::EarlyBinder(tcx.ty_error_with_message(
+ return ty::EarlyBinder::bind(Ty::new_error_with_message(
+ tcx,
DUMMY_SP,
"Could not collect return position impl trait in trait tys",
));
@@ -331,8 +331,6 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
}
}
- use rustc_hir::*;
-
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let icx = ItemCtxt::new(tcx, def_id);
@@ -341,13 +339,18 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
Node::TraitItem(item) => match item.kind {
TraitItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_fn_def(def_id.to_def_id(), substs)
+ Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
}
TraitItemKind::Const(ty, body_id) => body_id
.and_then(|body_id| {
is_suggestable_infer_ty(ty).then(|| {
infer_placeholder_type(
- tcx, def_id, body_id, ty.span, item.ident, "constant",
+ tcx,
+ def_id,
+ body_id,
+ ty.span,
+ item.ident,
+ "associated constant",
)
})
})
@@ -361,11 +364,18 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
Node::ImplItem(item) => match item.kind {
ImplItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_fn_def(def_id.to_def_id(), substs)
+ Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
}
ImplItemKind::Const(ty, body_id) => {
if is_suggestable_infer_ty(ty) {
- infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant")
+ infer_placeholder_type(
+ tcx,
+ def_id,
+ body_id,
+ ty.span,
+ item.ident,
+ "associated constant",
+ )
} else {
icx.to_ty(ty)
}
@@ -411,31 +421,31 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
span: spans.into(),
note: (),
});
- tcx.ty_error(guar)
+ Ty::new_error(tcx, guar)
}
_ => icx.to_ty(*self_ty),
},
ItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_fn_def(def_id.to_def_id(), substs)
+ Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
}
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
let def = tcx.adt_def(def_id);
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_adt(def, substs)
+ Ty::new_adt(tcx, def, substs)
}
ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { .. },
..
}) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
// Opaque types desugared from `impl Trait`.
- ItemKind::OpaqueTy(OpaqueTy {
+ ItemKind::OpaqueTy(&OpaqueTy {
origin:
hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
in_trait,
..
}) => {
- if in_trait && !tcx.impl_defaultness(owner).has_value() {
+ if in_trait && !tcx.defaultness(owner).has_value() {
span_bug!(
tcx.def_span(def_id),
"tried to get type of this RPITIT with no definition"
@@ -463,10 +473,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
Node::ForeignItem(foreign_item) => match foreign_item.kind {
ForeignItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_fn_def(def_id.to_def_id(), substs)
+ Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
}
ForeignItemKind::Static(t, _) => icx.to_ty(t),
- ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()),
+ ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
},
Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
@@ -475,7 +485,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
}
VariantData::Tuple(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id);
- tcx.mk_fn_def(def_id.to_def_id(), substs)
+ Ty::new_fn_def(tcx, def_id.to_def_id(), substs)
}
},
@@ -487,6 +497,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
Node::AnonConst(_) => anon_const_type_of(tcx, def_id),
+ Node::ConstBlock(_) => {
+ let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
+ substs.as_inline_const().ty()
+ }
+
Node::GenericParam(param) => match &param.kind {
GenericParamKind::Type { default: Some(ty), .. }
| GenericParamKind::Const { ty, .. } => icx.to_ty(ty),
@@ -497,7 +512,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
bug!("unexpected sort of node in type_of(): {:?}", x);
}
};
- ty::EarlyBinder(output)
+ ty::EarlyBinder::bind(output)
}
fn infer_placeholder_type<'a>(
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index f7c5b4467..957a6bb34 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -1,3 +1,4 @@
+use rustc_errors::StashKey;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem};
@@ -5,7 +6,7 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::DUMMY_SP;
-use crate::errors::UnconstrainedOpaqueType;
+use crate::errors::{TaitForwardCompat, UnconstrainedOpaqueType};
/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
/// laid for "higher-order pattern unification".
@@ -59,7 +60,20 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
}
}
- let Some(hidden) = locator.found else {
+ if let Some(hidden) = locator.found {
+ // Only check against typeck if we didn't already error
+ if !hidden.ty.references_error() {
+ for concrete_type in locator.typeck_types {
+ if concrete_type.ty != tcx.erase_regions(hidden.ty)
+ && !(concrete_type, hidden).references_error()
+ {
+ hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
+ }
+ }
+ }
+
+ hidden.ty
+ } else {
let reported = tcx.sess.emit_err(UnconstrainedOpaqueType {
span: tcx.def_span(def_id),
name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
@@ -70,21 +84,8 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
_ => "item",
},
});
- return tcx.ty_error(reported);
- };
-
- // Only check against typeck if we didn't already error
- if !hidden.ty.references_error() {
- for concrete_type in locator.typeck_types {
- if concrete_type.ty != tcx.erase_regions(hidden.ty)
- && !(concrete_type, hidden).references_error()
- {
- hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
- }
- }
+ Ty::new_error(tcx, reported)
}
-
- hidden.ty
}
struct TaitConstraintLocator<'tcx> {
@@ -127,16 +128,41 @@ impl TaitConstraintLocator<'_> {
// ```
let tables = self.tcx.typeck(item_def_id);
if let Some(guar) = tables.tainted_by_errors {
- self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error(guar) });
+ self.found =
+ Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: Ty::new_error(self.tcx, guar) });
return;
}
- let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else {
+
+ let mut constrained = false;
+ for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
+ if opaque_type_key.def_id != self.def_id {
+ continue;
+ }
+ constrained = true;
+ if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) {
+ self.tcx.sess.emit_err(TaitForwardCompat {
+ span: hidden_type.span,
+ item_span: self
+ .tcx
+ .def_ident_span(item_def_id)
+ .unwrap_or_else(|| self.tcx.def_span(item_def_id)),
+ });
+ }
+ let concrete_type =
+ self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params(
+ opaque_type_key,
+ self.tcx,
+ true,
+ ));
+ if self.typeck_types.iter().all(|prev| prev.ty != concrete_type.ty) {
+ self.typeck_types.push(concrete_type);
+ }
+ }
+
+ if !constrained {
debug!("no constraints in typeck results");
return;
};
- if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) {
- self.typeck_types.push(typeck_hidden_ty);
- }
// Use borrowck to get the type with unerased regions.
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
@@ -146,7 +172,7 @@ impl TaitConstraintLocator<'_> {
if let Some(prev) = &mut self.found {
if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
let guar = prev.report_mismatch(&concrete_type, self.def_id, self.tcx).emit();
- prev.ty = self.tcx.ty_error(guar);
+ prev.ty = Ty::new_error(self.tcx, guar);
}
} else {
self.found = Some(concrete_type);
@@ -190,17 +216,45 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
}
}
-pub(super) fn find_opaque_ty_constraints_for_rpit(
- tcx: TyCtxt<'_>,
+pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
+ tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
owner_def_id: LocalDefId,
) -> Ty<'_> {
- let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
+ let tables = tcx.typeck(owner_def_id);
- if let Some(concrete) = concrete {
+ // Check that all of the opaques we inferred during HIR are compatible.
+ // FIXME: We explicitly don't check that the types inferred during HIR
+ // typeck are compatible with the one that we infer during borrowck,
+ // because that one actually sometimes has consts evaluated eagerly so
+ // using strict type equality will fail.
+ let mut hir_opaque_ty: Option<ty::OpaqueHiddenType<'tcx>> = None;
+ if tables.tainted_by_errors.is_none() {
+ for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types {
+ if opaque_type_key.def_id != def_id {
+ continue;
+ }
+ let concrete_type = tcx.erase_regions(
+ hidden_type.remap_generic_params_to_declaration_params(opaque_type_key, tcx, true),
+ );
+ if let Some(prev) = &mut hir_opaque_ty {
+ if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
+ prev.report_mismatch(&concrete_type, def_id, tcx).stash(
+ tcx.def_span(opaque_type_key.def_id),
+ StashKey::OpaqueHiddenTypeMismatch,
+ );
+ }
+ } else {
+ hir_opaque_ty = Some(concrete_type);
+ }
+ }
+ }
+
+ let mir_opaque_ty = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
+ if let Some(mir_opaque_ty) = mir_opaque_ty {
let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id);
debug!(?scope);
- let mut locator = RpitConstraintChecker { def_id, tcx, found: concrete };
+ let mut locator = RpitConstraintChecker { def_id, tcx, found: mir_opaque_ty };
match tcx.hir().get(scope) {
Node::Item(it) => intravisit::walk_item(&mut locator, it),
@@ -208,27 +262,28 @@ pub(super) fn find_opaque_ty_constraints_for_rpit(
Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
other => bug!("{:?} is not a valid scope for an opaque type item", other),
}
- }
- concrete.map(|concrete| concrete.ty).unwrap_or_else(|| {
- let table = tcx.typeck(owner_def_id);
- if let Some(guar) = table.tainted_by_errors {
- // Some error in the
- // owner fn prevented us from populating
+ mir_opaque_ty.ty
+ } else {
+ if let Some(guar) = tables.tainted_by_errors {
+ // Some error in the owner fn prevented us from populating
// the `concrete_opaque_types` table.
- tcx.ty_error(guar)
+ Ty::new_error(tcx, guar)
} else {
- table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| {
+ // Fall back to the RPIT we inferred during HIR typeck
+ if let Some(hir_opaque_ty) = hir_opaque_ty {
+ hir_opaque_ty.ty
+ } else {
// We failed to resolve the opaque type or it
// resolves to itself. We interpret this as the
// no values of the hidden type ever being constructed,
// so we can just make the hidden type be `!`.
// For backwards compatibility reasons, we fall back to
// `()` until we the diverging default is changed.
- tcx.mk_diverging_default()
- })
+ Ty::new_diverging_default(tcx)
+ }
}
- })
+ }
}
struct RpitConstraintChecker<'tcx> {
diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 9200c2aec..35882ad35 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -151,7 +151,7 @@ pub fn identify_constrained_generic_params<'tcx>(
/// think of any.
pub fn setup_constraining_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
- predicates: &mut [(ty::Predicate<'tcx>, Span)],
+ predicates: &mut [(ty::Clause<'tcx>, Span)],
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
input_parameters: &mut FxHashSet<Parameter>,
) {
@@ -187,9 +187,7 @@ pub fn setup_constraining_predicates<'tcx>(
for j in i..predicates.len() {
// Note that we don't have to care about binders here,
// as the impl trait ref never contains any late-bound regions.
- if let ty::PredicateKind::Clause(ty::Clause::Projection(projection)) =
- predicates[j].0.kind().skip_binder()
- {
+ if let ty::ClauseKind::Projection(projection) = predicates[j].0.kind().skip_binder() {
// Special case: watch out for some kind of sneaky attempt
// to project out an associated type defined by this very
// trait.
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 6e7eb4f6c..c2d2e5f7e 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -5,7 +5,7 @@ use rustc_errors::{
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic,
MultiSpan,
};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty};
use rustc_span::{symbol::Ident, Span, Symbol};
@@ -184,6 +184,16 @@ pub struct UnconstrainedOpaqueType {
pub what: &'static str,
}
+#[derive(Diagnostic)]
+#[diag(hir_analysis_tait_forward_compat)]
+#[note]
+pub struct TaitForwardCompat {
+ #[primary_span]
+ pub span: Span,
+ #[note]
+ pub item_span: Span,
+}
+
pub struct MissingTypeParams {
pub span: Span,
pub def_span: Span,
@@ -857,3 +867,54 @@ pub(crate) enum DropImplPolarity {
span: Span,
},
}
+
+#[derive(Diagnostic)]
+pub(crate) enum ReturnTypeNotationIllegalParam {
+ #[diag(hir_analysis_return_type_notation_illegal_param_type)]
+ Type {
+ #[primary_span]
+ span: Span,
+ #[label]
+ param_span: Span,
+ },
+ #[diag(hir_analysis_return_type_notation_illegal_param_const)]
+ Const {
+ #[primary_span]
+ span: Span,
+ #[label]
+ param_span: Span,
+ },
+}
+
+#[derive(Diagnostic)]
+pub(crate) enum LateBoundInApit {
+ #[diag(hir_analysis_late_bound_type_in_apit)]
+ Type {
+ #[primary_span]
+ span: Span,
+ #[label]
+ param_span: Span,
+ },
+ #[diag(hir_analysis_late_bound_const_in_apit)]
+ Const {
+ #[primary_span]
+ span: Span,
+ #[label]
+ param_span: Span,
+ },
+ #[diag(hir_analysis_late_bound_lifetime_in_apit)]
+ Lifetime {
+ #[primary_span]
+ span: Span,
+ #[label]
+ param_span: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_unused_associated_type_bounds)]
+#[note]
+pub struct UnusedAssociatedTypeBounds {
+ #[suggestion(code = "")]
+ pub span: Span,
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index e4c6e6e39..f1765174d 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -79,7 +79,7 @@ fn diagnostic_hir_wf_check<'tcx>(
self.tcx,
cause,
self.param_env,
- ty::PredicateKind::WellFormed(tcx_ty.into()),
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(tcx_ty.into())),
));
for error in ocx.select_all_or_error() {
@@ -128,7 +128,9 @@ fn diagnostic_hir_wf_check<'tcx>(
ref item => bug!("Unexpected TraitItem {:?}", item),
},
hir::Node::Item(item) => match item.kind {
- hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => vec![ty],
+ hir::ItemKind::TyAlias(ty, _)
+ | hir::ItemKind::Static(ty, _, _)
+ | hir::ItemKind::Const(ty, _) => vec![ty],
hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
Some(t) => t
.path
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 612d4ff3d..5526dd4b0 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -106,10 +106,23 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
if item.defaultness(tcx).has_value() {
cgp::parameters_for(&tcx.type_of(def_id).subst_identity(), true)
} else {
- Vec::new()
+ vec![]
}
}
- ty::AssocKind::Fn | ty::AssocKind::Const => Vec::new(),
+ ty::AssocKind::Fn => {
+ if !tcx.lower_impl_trait_in_trait_to_assoc_ty()
+ && item.defaultness(tcx).has_value()
+ && tcx.impl_method_has_trait_impl_trait_tys(item.def_id)
+ && let Ok(table) = tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
+ {
+ table.values().copied().flat_map(|ty| {
+ cgp::parameters_for(&ty.subst_identity(), true)
+ }).collect()
+ } else {
+ vec![]
+ }
+ }
+ ty::AssocKind::Const => vec![],
}
})
.collect();
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index e84da2519..c64fb469b 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -77,7 +77,7 @@ use rustc_infer::traits::specialization_graph::Node;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
-use rustc_span::Span;
+use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, translate_substs_with_cause, wf, ObligationCtxt};
@@ -113,7 +113,7 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
let span = tcx.def_span(impl1_def_id);
check_has_items(tcx, impl1_def_id, impl2_node, span);
- if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
+ if let Ok((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
let impl2_def_id = impl2_node.def_id();
debug!(?impl2_def_id, ?impl2_substs);
@@ -171,16 +171,14 @@ fn get_impl_substs(
tcx: TyCtxt<'_>,
impl1_def_id: LocalDefId,
impl2_node: Node,
-) -> Option<(SubstsRef<'_>, SubstsRef<'_>)> {
+) -> Result<(SubstsRef<'_>, SubstsRef<'_>), ErrorGuaranteed> {
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
-
- let assumed_wf_types =
- ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
+ let impl1_span = tcx.def_span(impl1_def_id);
+ let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;
let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id);
- let impl1_span = tcx.def_span(impl1_def_id);
let impl2_substs = translate_substs_with_cause(
infcx,
param_env,
@@ -198,8 +196,8 @@ fn get_impl_substs(
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
- ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
- return None;
+ let guar = ocx.infcx.err_ctxt().report_fulfillment_errors(&errors);
+ return Err(guar);
}
let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
@@ -207,10 +205,10 @@ fn get_impl_substs(
let _ = ocx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
let span = tcx.def_span(impl1_def_id);
- tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
- return None;
+ let guar = tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
+ return Err(guar);
};
- Some((impl1_substs, impl2_substs))
+ Ok((impl1_substs, impl2_substs))
}
/// Returns a list of all of the unconstrained subst of the given impl.
@@ -235,10 +233,8 @@ fn unconstrained_parent_impl_substs<'tcx>(
// what we want here. We want only a list of constrained parameters while
// the functions in `cgp` add the constrained parameters to a list of
// unconstrained parameters.
- for (predicate, _) in impl_generic_predicates.predicates.iter() {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
- predicate.kind().skip_binder()
- {
+ for (clause, _) in impl_generic_predicates.predicates.iter() {
+ if let ty::ClauseKind::Projection(proj) = clause.kind().skip_binder() {
let projection_ty = proj.projection_ty;
let projected_ty = proj.term;
@@ -340,8 +336,11 @@ fn check_predicates<'tcx>(
impl2_substs: SubstsRef<'tcx>,
span: Span,
) {
- let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
- let impl1_predicates: Vec<_> = traits::elaborate(tcx, instantiated.into_iter()).collect();
+ let impl1_predicates: Vec<_> = traits::elaborate(
+ tcx,
+ tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs).into_iter(),
+ )
+ .collect();
let mut impl2_predicates = if impl2_node.is_from_trait() {
// Always applicable traits have to be always applicable without any
@@ -352,8 +351,8 @@ fn check_predicates<'tcx>(
tcx,
tcx.predicates_of(impl2_node.def_id())
.instantiate(tcx, impl2_substs)
- .predicates
- .into_iter(),
+ .into_iter()
+ .map(|(c, _s)| c.as_predicate()),
)
.collect()
};
@@ -377,13 +376,13 @@ fn check_predicates<'tcx>(
let always_applicable_traits = impl1_predicates
.iter()
.copied()
- .filter(|&(predicate, _)| {
+ .filter(|(clause, _span)| {
matches!(
- trait_predicate_kind(tcx, predicate),
+ trait_predicate_kind(tcx, clause.as_predicate()),
Some(TraitSpecializationKind::AlwaysApplicable)
)
})
- .map(|(pred, _span)| pred);
+ .map(|(c, _span)| c.as_predicate());
// Include the well-formed predicates of the type parameters of the impl.
for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity().substs {
@@ -398,9 +397,12 @@ fn check_predicates<'tcx>(
}
impl2_predicates.extend(traits::elaborate(tcx, always_applicable_traits));
- for (predicate, span) in impl1_predicates {
- if !impl2_predicates.iter().any(|pred2| trait_predicates_eq(tcx, predicate, *pred2, span)) {
- check_specialization_on(tcx, predicate, span)
+ for (clause, span) in impl1_predicates {
+ if !impl2_predicates
+ .iter()
+ .any(|pred2| trait_predicates_eq(tcx, clause.as_predicate(), *pred2, span))
+ {
+ check_specialization_on(tcx, clause.as_predicate(), span)
}
}
}
@@ -438,8 +440,8 @@ fn trait_predicates_eq<'tcx>(
let pred2_kind = predicate2.kind().skip_binder();
let (trait_pred1, trait_pred2) = match (pred1_kind, pred2_kind) {
(
- ty::PredicateKind::Clause(ty::Clause::Trait(pred1)),
- ty::PredicateKind::Clause(ty::Clause::Trait(pred2)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred1)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred2)),
) => (pred1, pred2),
// Just use plain syntactic equivalence if either of the predicates aren't
// trait predicates or have bound vars.
@@ -478,7 +480,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
_ if predicate.is_global() => (),
// We allow specializing on explicitly marked traits with no associated
// items.
- ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref,
constness: _,
polarity: _,
@@ -498,7 +500,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
.emit();
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_ty,
term,
})) => {
@@ -509,7 +511,7 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
)
.emit();
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => {
// FIXME(min_specialization), FIXME(const_generics):
// It probably isn't right to allow _every_ `ConstArgHasType` but I am somewhat unsure
// about the actual rules that would be sound. Can't just always error here because otherwise
@@ -532,24 +534,23 @@ fn trait_predicate_kind<'tcx>(
predicate: ty::Predicate<'tcx>,
) -> Option<TraitSpecializationKind> {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref,
constness: _,
polarity: _,
})) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::Projection(_))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::WellFormed(_)
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | ty::PredicateKind::Ambiguous => None,
}
}
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 5cd2cd50c..a68832d96 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -59,8 +59,6 @@ This API is completely unstable and subject to change.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
-#![feature(drain_filter)]
-#![feature(hash_drain_filter)]
#![feature(if_let_guard)]
#![feature(is_sorted)]
#![feature(iter_intersperse)]
@@ -321,16 +319,19 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
expected_return_type = main_fnsig.output();
} else {
// standard () main return type
- expected_return_type = ty::Binder::dummy(tcx.mk_unit());
+ expected_return_type = ty::Binder::dummy(Ty::new_unit(tcx));
}
if error {
return;
}
- let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
- tcx.mk_fn_sig([], expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
- }));
+ let se_ty = Ty::new_fn_ptr(
+ tcx,
+ expected_return_type.map_bound(|expected_return_type| {
+ tcx.mk_fn_sig([], expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
+ }),
+ );
require_same_types(
tcx,
@@ -341,7 +342,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
),
param_env,
se_ty,
- tcx.mk_fn_ptr(main_fnsig),
+ Ty::new_fn_ptr(tcx, main_fnsig),
);
}
fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
@@ -399,13 +400,16 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
}
}
- 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))],
- tcx.types.isize,
- false,
- hir::Unsafety::Normal,
- Abi::Rust,
- )));
+ let se_ty = Ty::new_fn_ptr(
+ tcx,
+ ty::Binder::dummy(tcx.mk_fn_sig(
+ [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
+ tcx.types.isize,
+ false,
+ hir::Unsafety::Normal,
+ Abi::Rust,
+ )),
+ );
require_same_types(
tcx,
@@ -416,7 +420,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
),
ty::ParamEnv::empty(), // start should not have any where bounds.
se_ty,
- tcx.mk_fn_ptr(tcx.fn_sig(start_def_id).subst_identity()),
+ Ty::new_fn_ptr(tcx, tcx.fn_sig(start_def_id).subst_identity()),
);
}
_ => {
diff --git a/compiler/rustc_hir_analysis/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
index 357deb07b..a7fca41f8 100644
--- a/compiler/rustc_hir_analysis/src/outlives/explicit.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
@@ -30,45 +30,34 @@ impl<'tcx> ExplicitPredicatesMap<'tcx> {
// process predicates and convert to `RequiredPredicates` entry, see below
for &(predicate, span) in predicates.predicates {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(OutlivesPredicate(
- ty,
- reg,
- ))) => insert_outlives_predicate(
- tcx,
- ty.into(),
- reg,
- span,
- &mut required_predicates,
- ),
-
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(OutlivesPredicate(
- reg1,
- reg2,
- ))) => insert_outlives_predicate(
- tcx,
- reg1.into(),
- reg2,
- span,
- &mut required_predicates,
- ),
-
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => (),
+ ty::ClauseKind::TypeOutlives(OutlivesPredicate(ty, reg)) => {
+ insert_outlives_predicate(
+ tcx,
+ ty.into(),
+ reg,
+ span,
+ &mut required_predicates,
+ )
+ }
+
+ ty::ClauseKind::RegionOutlives(OutlivesPredicate(reg1, reg2)) => {
+ insert_outlives_predicate(
+ tcx,
+ reg1.into(),
+ reg2,
+ span,
+ &mut required_predicates,
+ )
+ }
+ ty::ClauseKind::Trait(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => {}
}
}
- ty::EarlyBinder(required_predicates)
+ ty::EarlyBinder::bind(required_predicates)
})
}
}
diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index 0cd2fc1aa..71dca918f 100644
--- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -68,12 +68,13 @@ pub(super) fn infer_predicates(
// Therefore mark `predicates_added` as true and which will ensure
// we walk the crates again and re-calculate predicates for all
// items.
- let item_predicates_len: usize =
- global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len());
+ let item_predicates_len: usize = global_inferred_outlives
+ .get(&item_did.to_def_id())
+ .map_or(0, |p| p.as_ref().skip_binder().len());
if item_required_predicates.len() > item_predicates_len {
predicates_added = true;
global_inferred_outlives
- .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates));
+ .insert(item_did.to_def_id(), ty::EarlyBinder::bind(item_required_predicates));
}
}
@@ -137,7 +138,9 @@ fn insert_required_predicates_to_be_wf<'tcx>(
// 'a` holds for `Foo`.
debug!("Adt");
if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
- for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 {
+ for (unsubstituted_predicate, &span) in
+ unsubstituted_predicates.as_ref().skip_binder()
+ {
// `unsubstituted_predicate` is `U: 'b` in the
// example above. So apply the substitution to
// get `T: 'a` (or `predicate`):
@@ -251,7 +254,7 @@ fn check_explicit_predicates<'tcx>(
);
let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id);
- for (outlives_predicate, &span) in &explicit_predicates.0 {
+ for (outlives_predicate, &span) in explicit_predicates.as_ref().skip_binder() {
debug!("outlives_predicate = {:?}", &outlives_predicate);
// Careful: If we are inferring the effects of a `dyn Trait<..>`
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index a8596c707..48624cefe 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -3,7 +3,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::query::Providers;
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt};
+use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
@@ -20,7 +20,8 @@ pub fn provide(providers: &mut Providers) {
fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] {
let id = tcx.hir().local_def_id_to_hir_id(item_def_id);
- if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
+ if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst)
+ && tcx.features().generic_const_exprs
{
if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
// In `generics_of` we set the generics' parent to be our parent's parent which means that
@@ -51,9 +52,9 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
if tcx.has_attr(item_def_id, sym::rustc_outlives) {
let mut pred: Vec<String> = predicates
.iter()
- .map(|(out_pred, _)| match out_pred {
- ty::Clause::RegionOutlives(p) => p.to_string(),
- ty::Clause::TypeOutlives(p) => p.to_string(),
+ .map(|(out_pred, _)| match out_pred.kind().skip_binder() {
+ ty::ClauseKind::RegionOutlives(p) => p.to_string(),
+ ty::ClauseKind::TypeOutlives(p) => p.to_string(),
err => bug!("unexpected clause {:?}", err),
})
.collect();
@@ -98,24 +99,29 @@ fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
let predicates = global_inferred_outlives
.iter()
.map(|(&def_id, set)| {
- let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
- |(ty::OutlivesPredicate(kind1, region2), &span)| {
- match kind1.unpack() {
- GenericArgKind::Type(ty1) => Some((
- ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)),
- span,
- )),
- GenericArgKind::Lifetime(region1) => Some((
- ty::Clause::RegionOutlives(ty::OutlivesPredicate(region1, *region2)),
- span,
- )),
- GenericArgKind::Const(_) => {
- // Generic consts don't impose any constraints.
- None
+ let predicates =
+ &*tcx.arena.alloc_from_iter(set.as_ref().skip_binder().iter().filter_map(
+ |(ty::OutlivesPredicate(kind1, region2), &span)| {
+ match kind1.unpack() {
+ GenericArgKind::Type(ty1) => Some((
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty1, *region2))
+ .to_predicate(tcx),
+ span,
+ )),
+ GenericArgKind::Lifetime(region1) => Some((
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
+ region1, *region2,
+ ))
+ .to_predicate(tcx),
+ span,
+ )),
+ GenericArgKind::Const(_) => {
+ // Generic consts don't impose any constraints.
+ None
+ }
}
- }
- },
- ));
+ },
+ ));
(def_id, predicates)
})
.collect();
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 3ebd9e134..066e74491 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -51,20 +51,19 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
| DefKind::Struct
| DefKind::Union
| DefKind::Variant
- | DefKind::Ctor(..) => {}
+ | DefKind::Ctor(..) => {
+ // These are inferred.
+ let crate_map = tcx.crate_variances(());
+ return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);
+ }
DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder => {
return variance_of_opaque(tcx, item_def_id);
}
- _ => {
- // Variance not relevant.
- span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item")
- }
+ _ => {}
}
- // Everything else must be inferred.
-
- let crate_map = tcx.crate_variances(());
- crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
+ // Variance not relevant.
+ span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item");
}
#[instrument(level = "trace", skip(tcx), ret)]
@@ -119,7 +118,8 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) check whether this is necessary
// at all for RPITITs.
ty::Alias(_, ty::AliasTy { def_id, substs, .. })
- if self.tcx.is_impl_trait_in_trait(*def_id) =>
+ if self.tcx.is_impl_trait_in_trait(*def_id)
+ && !self.tcx.lower_impl_trait_in_trait_to_assoc_ty() =>
{
self.visit_opaque(*def_id, substs)
}
@@ -162,28 +162,25 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
// which thus mentions `'a` and should thus accept hidden types that borrow 'a
// instead of requiring an additional `+ 'a`.
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref: ty::TraitRef { def_id: _, substs, .. },
constness: _,
polarity: _,
- })) => {
+ }) => {
for subst in &substs[1..] {
subst.visit_with(&mut collector);
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate {
+ ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_ty: ty::AliasTy { substs, .. },
term,
- })) => {
+ }) => {
for subst in &substs[1..] {
subst.visit_with(&mut collector);
}
term.visit_with(&mut collector);
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
- _,
- region,
- ))) => {
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(_, region)) => {
region.visit_with(&mut collector);
}
_ => {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index d93e8efc1..a699cd6c9 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -84,6 +84,7 @@ impl<'a> State<'a> {
Node::ImplItem(a) => self.print_impl_item(a),
Node::Variant(a) => self.print_variant(a),
Node::AnonConst(a) => self.print_anon_const(a),
+ Node::ConstBlock(a) => self.print_inline_const(a),
Node::Expr(a) => self.print_expr(a),
Node::ExprField(a) => self.print_expr_field(&a),
Node::Stmt(a) => self.print_stmt(a),
@@ -1095,10 +1096,10 @@ impl<'a> State<'a> {
self.end()
}
- fn print_expr_anon_const(&mut self, anon_const: &hir::AnonConst) {
+ fn print_inline_const(&mut self, constant: &hir::ConstBlock) {
self.ibox(INDENT_UNIT);
self.word_space("const");
- self.print_anon_const(anon_const);
+ self.ann.nested(self, Nested::Body(constant.body));
self.end()
}
@@ -1370,7 +1371,7 @@ impl<'a> State<'a> {
self.print_expr_vec(exprs);
}
hir::ExprKind::ConstBlock(ref anon_const) => {
- self.print_expr_anon_const(anon_const);
+ self.print_inline_const(anon_const);
}
hir::ExprKind::Repeat(element, ref count) => {
self.print_expr_repeat(element, count);
@@ -1553,6 +1554,11 @@ impl<'a> State<'a> {
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
}
}
+ hir::ExprKind::Become(result) => {
+ self.word("become");
+ self.word(" ");
+ self.print_expr_maybe_paren(result, parser::PREC_JUMP);
+ }
hir::ExprKind::InlineAsm(asm) => {
self.word("asm!");
self.print_inline_asm(asm);
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index aab432eee..3d012a15a 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item
hir_typeck_convert_to_str = try converting the passed type into a `&str`
+hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}`
+
hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
hir_typeck_expected_default_return_type = expected `()` because of default return type
@@ -76,8 +78,8 @@ hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang
hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters
hir_typeck_return_stmt_outside_of_fn_body =
- return statement outside of function body
- .encl_body_label = the return is part of this body...
+ {$statement_kind} statement outside of function body
+ .encl_body_label = the {$statement_kind} is part of this body...
.encl_fn_label = ...not the enclosing function body
hir_typeck_struct_expr_non_exhaustive =
@@ -87,6 +89,8 @@ hir_typeck_suggest_boxing_note = for more on the distinction between the stack a
hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `Box::new`
+hir_typeck_suggest_ptr_null_mut = consider using `core::ptr::null_mut` instead
+
hir_typeck_union_pat_dotdot = `..` cannot be used in union patterns
hir_typeck_union_pat_multiple_fields = union patterns should have exactly one field
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 7d2f7e876..e8720a5da 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// us to give better error messages (pointing to a usually better
// arm for inconsistent arms or to the whole match when a `()` type
// is required).
- Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety,
+ Expectation::ExpectHasType(ety) if ety != Ty::new_unit(self.tcx) => ety,
_ => self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: expr.span,
@@ -510,6 +510,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
..
} = self.type_var_origin(expected)? else { return None; };
+ let Some(rpit_local_def_id) = rpit_def_id.as_local() else { return None; };
+ if !matches!(
+ self.tcx.hir().expect_item(rpit_local_def_id).expect_opaque_ty().origin,
+ hir::OpaqueTyOrigin::FnReturn(..)
+ ) {
+ return None;
+ }
+
let sig = self.body_fn_sig()?;
let substs = sig.output().walk().find_map(|arg| {
@@ -528,31 +536,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
for ty in [first_ty, second_ty] {
- for (pred, _) in self
+ for (clause, _) in self
.tcx
.explicit_item_bounds(rpit_def_id)
.subst_iter_copied(self.tcx, substs)
{
- let pred = pred.kind().rebind(match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => {
+ let pred = clause.kind().rebind(match clause.kind().skip_binder() {
+ ty::ClauseKind::Trait(trait_pred) => {
// FIXME(rpitit): This will need to be fixed when we move to associated types
assert!(matches!(
*trait_pred.trait_ref.self_ty().kind(),
ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. })
if def_id == rpit_def_id && substs == alias_substs
));
- ty::PredicateKind::Clause(ty::Clause::Trait(
- trait_pred.with_self_ty(self.tcx, ty),
- ))
+ ty::ClauseKind::Trait(trait_pred.with_self_ty(self.tcx, ty))
}
- ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => {
+ ty::ClauseKind::Projection(mut proj_pred) => {
assert!(matches!(
*proj_pred.projection_ty.self_ty().kind(),
ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. })
if def_id == rpit_def_id && substs == alias_substs
));
proj_pred = proj_pred.with_self_ty(self.tcx, ty);
- ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred))
+ ty::ClauseKind::Projection(proj_pred)
}
_ => continue,
});
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 655ab94eb..f306653c1 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -6,8 +6,9 @@ use crate::type_error_struct;
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey};
use rustc_hir as hir;
-use rustc_hir::def::{self, CtorKind, Namespace, Res};
+use rustc_hir::def::{self, CtorKind, DefKind, Namespace, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir::HirId;
use rustc_hir_analysis::autoderef::Autoderef;
use rustc_infer::{
infer,
@@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => self.check_expr(callee_expr),
};
- let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
+ let expr_ty = self.structurally_resolve_type(call_expr.span, original_callee_ty);
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
let mut result = None;
@@ -138,7 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
autoderef: &Autoderef<'a, 'tcx>,
) -> Option<CallStep<'tcx>> {
let adjusted_ty =
- self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+ self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
// If the callee is a bare function or a closure, then we're all set.
match *adjusted_ty.kind() {
@@ -232,12 +233,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some(trait_def_id) = opt_trait_def_id else { continue };
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
- self.tcx.mk_tup_from_iter(arg_exprs.iter().map(|e| {
- self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeInference,
- span: e.span,
- })
- }))
+ Ty::new_tup_from_iter(
+ self.tcx,
+ arg_exprs.iter().map(|e| {
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::TypeInference,
+ span: e.span,
+ })
+ }),
+ )
});
if let Some(ok) = self.lookup_method_in_trait(
@@ -376,15 +380,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let (fn_sig, def_id) = match *callee_ty.kind() {
- ty::FnDef(def_id, subst) => {
- let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst);
+ ty::FnDef(def_id, substs) => {
+ self.enforce_context_effects(call_expr.hir_id, call_expr.span, def_id, substs);
+ let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs);
// Unit testing: function items annotated with
// `#[rustc_evaluate_where_clauses]` trigger special output
// to let us test the trait evaluation system.
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
let predicates = self.tcx.predicates_of(def_id);
- let predicates = predicates.instantiate(self.tcx, subst);
+ let predicates = predicates.instantiate(self.tcx, substs);
for (predicate, predicate_span) in predicates {
let obligation = Obligation::new(
self.tcx,
@@ -405,6 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
(fn_sig, Some(def_id))
}
+ // FIXME(effects): these arms should error because we can't enforce them
ty::FnPtr(sig) => (sig, None),
_ => {
for arg in arg_exprs {
@@ -420,25 +426,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
{
// Try suggesting `foo(a)` -> `a.foo()` if possible.
- if let Some(ty) =
- self.suggest_call_as_method(
- &mut diag,
- segment,
- arg_exprs,
- call_expr,
- expected
- )
- {
- diag.emit();
- return ty;
- } else {
- diag.emit();
- }
+ self.suggest_call_as_method(
+ &mut diag,
+ segment,
+ arg_exprs,
+ call_expr,
+ expected
+ );
+ diag.emit();
}
let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
- return self.tcx.ty_error(err);
+ return Ty::new_error(self.tcx, err);
}
};
@@ -476,6 +476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.require_lang_item(hir::LangItem::Tuple, Some(sp)),
traits::ObligationCause::new(sp, self.body_id, traits::RustCall),
);
+ self.require_type_is_sized(ty, sp, traits::RustCall);
} else {
self.tcx.sess.span_err(
sp,
@@ -496,9 +497,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
arg_exprs: &'tcx [hir::Expr<'tcx>],
call_expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
- ) -> Option<Ty<'tcx>> {
+ ) {
if let [callee_expr, rest @ ..] = arg_exprs {
- let callee_ty = self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)?;
+ let Some(callee_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr) else {
+ return;
+ };
// First, do a probe with `IsSuggestion(true)` to avoid emitting
// any strange errors. If it's successful, then we'll do a true
@@ -513,7 +516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ProbeScope::AllTraits,
expected.only_has_type(self),
) else {
- return None;
+ return;
};
let pick = self.confirm_method(
@@ -525,7 +528,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
segment,
);
if pick.illegal_sized_bound.is_some() {
- return None;
+ return;
}
let up_to_rcvr_span = segment.ident.span.until(callee_expr.span);
@@ -567,22 +570,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg,
Applicability::MaybeIncorrect,
);
-
- // Let's check the method fully now
- let return_ty = self.check_method_argument_types(
- segment.ident.span,
- call_expr,
- Ok(pick.callee),
- rest,
- TupleArgumentsFlag::DontTupleArguments,
- expected,
- );
-
- return Some(return_ty);
}
}
-
- None
}
fn report_invalid_callee(
@@ -756,6 +745,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn_sig.output()
}
+ #[tracing::instrument(level = "debug", skip(self, span))]
+ pub(super) fn enforce_context_effects(
+ &self,
+ call_expr_hir: HirId,
+ span: Span,
+ callee_did: DefId,
+ callee_substs: SubstsRef<'tcx>,
+ ) {
+ let tcx = self.tcx;
+
+ if !tcx.features().effects || tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you {
+ return;
+ }
+
+ // Compute the constness required by the context.
+ let context = tcx.hir().enclosing_body_owner(call_expr_hir);
+ let const_context = tcx.hir().body_const_context(context);
+
+ let kind = tcx.def_kind(context.to_def_id());
+ debug_assert_ne!(kind, DefKind::ConstParam);
+
+ if tcx.has_attr(context.to_def_id(), sym::rustc_do_not_const_check) {
+ trace!("do not const check this context");
+ return;
+ }
+
+ let effect = match const_context {
+ Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => tcx.consts.false_,
+ Some(hir::ConstContext::ConstFn) => {
+ let substs = ty::InternalSubsts::identity_for_item(tcx, context);
+ substs.host_effect_param().expect("ConstContext::Maybe must have host effect param")
+ }
+ None => tcx.consts.true_,
+ };
+
+ let generics = tcx.generics_of(callee_did);
+
+ trace!(?effect, ?generics, ?callee_substs);
+
+ if let Some(idx) = generics.host_effect_index {
+ let param = callee_substs.const_at(idx);
+ let cause = self.misc(span);
+ match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::No, effect, param) {
+ Ok(infer::InferOk { obligations, value: () }) => {
+ self.register_predicates(obligations);
+ }
+ Err(e) => {
+ // FIXME(effects): better diagnostic
+ self.err_ctxt().report_mismatched_consts(&cause, effect, param, e).emit();
+ }
+ }
+ }
+ }
+
fn confirm_overloaded_call(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 98c683f02..633933317 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -103,15 +103,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(match *t.kind() {
ty::Slice(_) | ty::Str => Some(PointerKind::Length),
ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
- ty::Adt(def, substs) if def.is_struct() => {
- match def.non_enum_variant().fields.raw.last() {
- None => Some(PointerKind::Thin),
- Some(f) => {
- let field_ty = self.field_ty(span, f, substs);
- self.pointer_kind(field_ty, span)?
- }
+ ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().tail_opt() {
+ None => Some(PointerKind::Thin),
+ Some(f) => {
+ let field_ty = self.field_ty(span, f, substs);
+ self.pointer_kind(field_ty, span)?
}
- }
+ },
ty::Tuple(fields) => match fields.last() {
None => Some(PointerKind::Thin),
Some(&f) => self.pointer_kind(f, span)?,
@@ -393,7 +391,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
&& fcx
.try_coerce(
self.expr,
- fcx.tcx.mk_ref(
+ Ty::new_ref(fcx.tcx,
fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: expr_ty, mutbl },
),
@@ -410,7 +408,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
&& fcx
.try_coerce(
self.expr,
- fcx.tcx.mk_ref(
+ Ty::new_ref(fcx.tcx,
expr_reg,
TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut },
),
@@ -428,7 +426,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
&& fcx
.try_coerce(
self.expr,
- fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
+ Ty::new_ref(fcx.tcx,reg, TypeAndMut { ty: self.expr_ty, mutbl }),
self.cast_ty,
AllowTwoPhase::No,
None,
@@ -441,7 +439,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
&& fcx
.try_coerce(
self.expr,
- fcx.tcx.mk_ref(
+ Ty::new_ref(fcx.tcx,
fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: self.expr_ty, mutbl },
),
@@ -689,8 +687,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
let t_cast = self.cast_ty;
let t_expr = self.expr_ty;
- let type_asc_or =
- if fcx.tcx.features().type_ascription { "type ascription or " } else { "" };
let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() {
("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS)
} else {
@@ -711,7 +707,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|lint| {
lint.help(format!(
"cast can be replaced by coercion; this might \
- require {type_asc_or}a temporary variable"
+ require a temporary variable"
))
},
);
@@ -719,8 +715,8 @@ impl<'a, 'tcx> CastCheck<'tcx> {
#[instrument(skip(fcx), level = "debug")]
pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
- self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty);
- self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty);
+ self.expr_ty = fcx.structurally_resolve_type(self.expr_span, self.expr_ty);
+ self.cast_ty = fcx.structurally_resolve_type(self.cast_span, self.cast_ty);
debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);
@@ -767,7 +763,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
let res = fcx.try_coerce(
self.expr,
self.expr_ty,
- fcx.tcx.mk_fn_ptr(f),
+ Ty::new_fn_ptr(fcx.tcx, f),
AllowTwoPhase::No,
None,
);
@@ -959,7 +955,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
// from a region pointer to a vector.
// Coerce to a raw pointer so that we generate AddressOf in MIR.
- let array_ptr_type = fcx.tcx.mk_ptr(m_expr);
+ let array_ptr_type = Ty::new_ptr(fcx.tcx, m_expr);
fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
.unwrap_or_else(|_| {
bug!(
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index bfabd44bb..8b57e311f 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -62,11 +62,11 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
yield_ty
} else {
- tcx.mk_unit()
+ Ty::new_unit(tcx,)
};
// Resume type defaults to `()` if the generator has no argument.
- let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit());
+ let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| Ty::new_unit(tcx,));
fcx.resume_yield_tys = Some((resume_ty, yield_ty));
}
@@ -92,11 +92,20 @@ pub(super) fn check_fn<'a, 'tcx>(
fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
// Check that argument is Sized.
- // 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() && !params_can_be_unsized {
- fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span));
+ if !params_can_be_unsized {
+ fcx.require_type_is_sized(
+ param_ty,
+ param.pat.span,
+ // ty_span == binding_span iff this is a closure parameter with no type ascription,
+ // or if it's an implicit `self` parameter
+ traits::SizedArgumentType(
+ if ty_span == Some(param.span) && tcx.is_closure(fn_def_id.into()) {
+ None
+ } else {
+ ty_span
+ },
+ ),
+ );
}
fcx.write_ty(param.hir_id, param_ty);
@@ -247,10 +256,10 @@ fn check_lang_start_fn<'tcx>(
// for example `start`'s generic should be a type parameter
let generics = tcx.generics_of(def_id);
let fn_generic = generics.param_at(0, tcx);
- let generic_ty = tcx.mk_ty_param(fn_generic.index, fn_generic.name);
+ let generic_ty = Ty::new_param(tcx, fn_generic.index, fn_generic.name);
let expected_fn_sig =
tcx.mk_fn_sig([], generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
- let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
+ let expected_ty = Ty::new_fn_ptr(tcx, Binder::dummy(expected_fn_sig));
// we emit the same error to suggest changing the arg no matter what's wrong with the arg
let emit_main_fn_arg_err = || {
@@ -307,9 +316,9 @@ fn check_lang_start_fn<'tcx>(
if !argv_is_okay {
let inner_ptr_ty =
- tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
+ Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
let expected_ty =
- tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
+ Ty::new_ptr(tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
tcx.sess.emit_err(LangStartIncorrectParam {
param_span: decl.inputs[2].span,
param_num: 3,
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 9659a0ec1..78a9ac49d 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -98,7 +98,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
);
- let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
+ let tupled_upvars_ty = self.next_root_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: self.tcx.def_span(expr_def_id),
});
@@ -117,7 +117,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
);
- return self.tcx.mk_generator(
+ return Ty::new_generator(
+ self.tcx,
expr_def_id.to_def_id(),
generator_substs.substs,
movability,
@@ -128,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the `closures` table.
let sig = bound_sig.map_bound(|sig| {
self.tcx.mk_fn_sig(
- [self.tcx.mk_tup(sig.inputs())],
+ [Ty::new_tup(self.tcx, sig.inputs())],
sig.output(),
sig.c_variadic,
sig.unsafety,
@@ -143,7 +144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Create a type variable (for now) to represent the closure kind.
// It will be unified during the upvar inference phase (`upvar.rs`)
- None => self.next_ty_var(TypeVariableOrigin {
+ None => self.next_root_ty_var(TypeVariableOrigin {
// FIXME(eddyb) distinguish closure kind inference variables from the rest.
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
@@ -155,12 +156,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::ClosureSubstsParts {
parent_substs,
closure_kind_ty,
- closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig),
+ closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(self.tcx, sig),
tupled_upvars_ty,
},
);
- self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs)
+ Ty::new_closure(self.tcx, expr_def_id.to_def_id(), closure_substs.substs)
}
/// Given the expected type, figures out what it can about this closure we
@@ -174,7 +175,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
.deduce_closure_signature_from_predicates(
expected_ty,
- self.tcx.explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
+ self.tcx
+ .explicit_item_bounds(def_id)
+ .subst_iter_copied(self.tcx, substs)
+ .map(|(c, s)| (c.as_predicate(), s)),
),
ty::Dynamic(ref object_type, ..) => {
let sig = object_type.projection_bounds().find_map(|pb| {
@@ -187,7 +191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(sig, kind)
}
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
- self.tcx.mk_ty_var(self.root_var(vid)),
+ Ty::new_var(self.tcx, self.root_var(vid)),
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
),
ty::FnPtr(sig) => {
@@ -222,7 +226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Given a Projection predicate, we can potentially infer
// the complete signature.
if expected_sig.is_none()
- && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder()
+ && let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj_predicate)) = bound_predicate.skip_binder()
{
let inferred_sig = self.normalize(
span,
@@ -258,10 +262,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// like `F : Fn<A>`. Note that due to subtyping we could encounter
// many viable options, so pick the most restrictive.
let trait_def_id = match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
Some(data.projection_ty.trait_def_id(self.tcx))
}
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => Some(data.def_id()),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => Some(data.def_id()),
_ => None,
};
if let Some(closure_kind) =
@@ -695,7 +699,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// where R is the return type we are expecting. This type `T`
// will be our output.
let bound_predicate = predicate.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj_predicate)) =
bound_predicate.skip_binder()
{
self.deduce_future_output_from_projection(
@@ -717,13 +721,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.tcx
.explicit_item_bounds(def_id)
.subst_iter_copied(self.tcx, substs)
- .find_map(|(p, s)| get_future_output(p, s))?,
+ .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?,
ty::Error(_) => return None,
ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => self
.tcx
.explicit_item_bounds(proj.def_id)
.subst_iter_copied(self.tcx, proj.substs)
- .find_map(|(p, s)| get_future_output(p, s))?,
+ .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?,
_ => span_bug!(
self.tcx.def_span(expr_def_id),
"async fn generator return type not an inference variable: {ret_ty}"
@@ -803,7 +807,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
guar: ErrorGuaranteed,
) -> ty::PolyFnSig<'tcx> {
let astconv: &dyn AstConv<'_> = self;
- let err_ty = self.tcx.ty_error(guar);
+ let err_ty = Ty::new_error(self.tcx, guar);
let supplied_arguments = decl.inputs.iter().map(|a| {
// Convert the types that the user supplied (if any), but ignore them.
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 08c4082e8..dc58d99ed 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -36,9 +36,7 @@
//! ```
use crate::FnCtxt;
-use rustc_errors::{
- struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
-};
+use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
@@ -49,7 +47,7 @@ use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
use rustc_infer::traits::{Obligation, PredicateObligation};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
+ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::relate::RelateResult;
@@ -58,10 +56,11 @@ use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TypeAndMut};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
-use rustc_span::{self, BytePos, DesugaringKind, Span};
+use rustc_span::{self, DesugaringKind};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
};
@@ -144,12 +143,28 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub);
self.commit_if_ok(|_| {
let at = self.at(&self.cause, self.fcx.param_env);
- if self.use_lub {
+
+ let res = if self.use_lub {
at.lub(DefineOpaqueTypes::Yes, b, a)
} else {
at.sup(DefineOpaqueTypes::Yes, b, a)
.map(|InferOk { value: (), obligations }| InferOk { value: a, obligations })
+ };
+
+ // In the new solver, lazy norm may allow us to shallowly equate
+ // more types, but we emit possibly impossible-to-satisfy obligations.
+ // Filter these cases out to make sure our coercion is more accurate.
+ if self.next_trait_solver() {
+ if let Ok(res) = &res {
+ for obligation in &res.obligations {
+ if !self.predicate_may_hold(&obligation) {
+ return Err(TypeError::Mismatch);
+ }
+ }
+ }
}
+
+ res
})
}
@@ -177,7 +192,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
let _ = self.commit_if_ok(|_| {
self.at(&self.cause, self.param_env).eq(DefineOpaqueTypes::Yes, a, b)
});
- return success(vec![], self.fcx.tcx.ty_error(guar), vec![]);
+ return success(vec![], Ty::new_error(self.fcx.tcx, guar), vec![]);
}
// Coercing from `!` to any type is allowed:
@@ -425,7 +440,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
}
r_borrow_var.unwrap()
};
- let derefd_ty_a = self.tcx.mk_ref(
+ let derefd_ty_a = Ty::new_ref(
+ self.tcx,
r,
TypeAndMut {
ty: referent_ty,
@@ -543,9 +559,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Adjustment { kind: Adjust::Deref(None), target: ty_a },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
- target: self
- .tcx
- .mk_ref(r_borrow, ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }),
+ target: Ty::new_ref(
+ self.tcx,
+ r_borrow,
+ ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a },
+ ),
},
))
}
@@ -556,7 +574,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Adjustment { kind: Adjust::Deref(None), target: ty_a },
Adjustment {
kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)),
- target: self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mt_b, ty: ty_a }),
+ target: Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mt_b, ty: ty_a }),
},
))
}
@@ -574,7 +592,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
};
let coerce_target = self.next_ty_var(origin);
let mut coercion = self.unify_and(coerce_target, target, |target| {
- let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target };
+ let unsize = Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), target };
match reborrow {
None => vec![unsize],
Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize],
@@ -614,9 +632,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
while !queue.is_empty() {
let obligation = queue.remove(0);
debug!("coerce_unsized resolve step: {:?}", obligation);
- let bound_predicate = obligation.predicate.kind();
- let trait_pred = match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
+ let trait_pred = match obligation.predicate.kind().no_bound_vars() {
+ Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)))
if traits.contains(&trait_pred.def_id()) =>
{
if unsize_did == trait_pred.def_id() {
@@ -634,7 +651,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
has_unsized_tuple_coercion = true;
}
}
- bound_predicate.rebind(trait_pred)
+ trait_pred
}
_ => {
coercion.obligations.push(obligation);
@@ -646,8 +663,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
Ok(None) => {
if trait_pred.def_id() == unsize_did {
let trait_pred = self.resolve_vars_if_possible(trait_pred);
- let self_ty = trait_pred.skip_binder().self_ty();
- let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty();
+ let self_ty = trait_pred.self_ty();
+ let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty();
debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
match (self_ty.kind(), unsize_ty.kind()) {
(&ty::Infer(ty::TyVar(v)), ty::Dynamic(..))
@@ -752,7 +769,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.tcx,
self.cause.clone(),
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
ty::OutlivesPredicate(a, b_region),
))),
),
@@ -791,6 +808,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
{
self.commit_if_ok(|snapshot| {
+ let outer_universe = self.infcx.universe();
+
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
@@ -807,7 +826,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// want the coerced type to be the actual supertype of these two,
// but for now, we want to just error to ensure we don't lock
// ourselves into a specific behavior with NLL.
- self.leak_check(false, snapshot)?;
+ self.leak_check(outer_universe, Some(snapshot))?;
result
})
@@ -830,7 +849,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
a,
fn_ty_a,
b,
- simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)),
+ simple(Adjust::Pointer(PointerCoercion::UnsafeFnPointer)),
identity,
)
}
@@ -866,7 +885,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
self.at(&self.cause, self.param_env).normalize(a_sig);
obligations.extend(o1);
- let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig);
+ let a_fn_pointer = Ty::new_fn_ptr(self.tcx, a_sig);
let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn(
a_fn_pointer,
a_sig,
@@ -874,16 +893,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|unsafe_ty| {
vec![
Adjustment {
- kind: Adjust::Pointer(PointerCast::ReifyFnPointer),
+ kind: Adjust::Pointer(PointerCoercion::ReifyFnPointer),
target: a_fn_pointer,
},
Adjustment {
- kind: Adjust::Pointer(PointerCast::UnsafeFnPointer),
+ kind: Adjust::Pointer(PointerCoercion::UnsafeFnPointer),
target: unsafe_ty,
},
]
},
- simple(Adjust::Pointer(PointerCast::ReifyFnPointer)),
+ simple(Adjust::Pointer(PointerCoercion::ReifyFnPointer)),
)?;
obligations.extend(o2);
@@ -928,12 +947,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
let closure_sig = substs_a.as_closure().sig();
let unsafety = fn_ty.unsafety();
let pointer_ty =
- self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety));
+ Ty::new_fn_ptr(self.tcx, self.tcx.signature_unclosure(closure_sig, unsafety));
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty);
self.unify_and(
pointer_ty,
b,
- simple(Adjust::Pointer(PointerCast::ClosureFnPointer(unsafety))),
+ simple(Adjust::Pointer(PointerCoercion::ClosureFnPointer(unsafety))),
)
}
_ => self.unify_and(a, b, identity),
@@ -956,7 +975,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
coerce_mutbls(mt_a.mutbl, mutbl_b)?;
// Check that the types which they point at are compatible.
- let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
+ let a_unsafe = Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty });
// Although references and unsafe ptrs have the same
// representation, we still register an Adjust::DerefRef so that
// regionck knows that the region for `a` must be valid here.
@@ -968,7 +987,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
]
})
} else if mt_a.mutbl != mutbl_b {
- self.unify_and(a_unsafe, b, simple(Adjust::Pointer(PointerCast::MutToConstPointer)))
+ self.unify_and(a_unsafe, b, simple(Adjust::Pointer(PointerCoercion::MutToConstPointer)))
} else {
self.unify_and(a_unsafe, b, identity)
}
@@ -988,7 +1007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
allow_two_phase: AllowTwoPhase,
cause: Option<ObligationCause<'tcx>>,
) -> RelateResult<'tcx, Ty<'tcx>> {
- let source = self.resolve_vars_with_obligations(expr_ty);
+ let source = self.try_structurally_resolve_type(expr.span, expr_ty);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
let cause =
@@ -998,7 +1017,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (adjustments, _) = self.register_infer_ok_obligations(ok);
self.apply_adjustments(expr, adjustments);
- Ok(if let Err(guar) = expr_ty.error_reported() { self.tcx.ty_error(guar) } else { target })
+ Ok(if let Err(guar) = expr_ty.error_reported() {
+ Ty::new_error(self.tcx, guar)
+ } else {
+ target
+ })
}
/// Same as `try_coerce()`, but without side-effects.
@@ -1016,7 +1039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Ok(ok) = coerce.coerce(source, target) else {
return false;
};
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
ocx.register_obligations(ok.obligations);
ocx.select_where_possible().is_empty()
})
@@ -1162,15 +1185,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.map(|ok| self.register_infer_ok_obligations(ok))?;
// Reify both sides and return the reified fn pointer type.
- let fn_ptr = self.tcx.mk_fn_ptr(sig);
+ let fn_ptr = Ty::new_fn_ptr(self.tcx, sig);
let prev_adjustment = match prev_ty.kind() {
- ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())),
- ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+ ty::Closure(..) => {
+ Adjust::Pointer(PointerCoercion::ClosureFnPointer(a_sig.unsafety()))
+ }
+ ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer),
_ => unreachable!(),
};
let next_adjustment = match new_ty.kind() {
- ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())),
- ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer),
+ ty::Closure(..) => {
+ Adjust::Pointer(PointerCoercion::ClosureFnPointer(b_sig.unsafety()))
+ }
+ ty::FnDef(..) => Adjust::Pointer(PointerCoercion::ReifyFnPointer),
_ => unreachable!(),
};
for expr in exprs.iter().map(|e| e.as_coercion_site()) {
@@ -1413,7 +1440,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fcx,
cause,
None,
- fcx.tcx.mk_unit(),
+ Ty::new_unit(fcx.tcx),
Some(augment_error),
label_unit_as_expected,
)
@@ -1444,7 +1471,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// If we see any error types, just propagate that error
// upwards.
if let Err(guar) = (expression_ty, self.merged_ty()).error_reported() {
- self.final_ty = Some(fcx.tcx.ty_error(guar));
+ self.final_ty = Some(Ty::new_error(fcx.tcx, guar));
return;
}
@@ -1576,7 +1603,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
Some(blk_id),
);
if !fcx.tcx.features().unsized_locals {
- unsized_return = self.is_return_ty_unsized(fcx, blk_id);
+ unsized_return = self.is_return_ty_definitely_unsized(fcx);
}
if let Some(expression) = expression
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
@@ -1595,8 +1622,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
None,
);
if !fcx.tcx.features().unsized_locals {
- let id = fcx.tcx.hir().parent_id(id);
- unsized_return = self.is_return_ty_unsized(fcx, id);
+ unsized_return = self.is_return_ty_definitely_unsized(fcx);
}
}
_ => {
@@ -1633,7 +1659,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
let reported = err.emit_unless(unsized_return);
- self.final_ty = Some(fcx.tcx.ty_error(reported));
+ self.final_ty = Some(Ty::new_error(fcx.tcx, reported));
}
}
}
@@ -1684,9 +1710,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
- let mut pointing_at_return_type = false;
- let mut fn_output = None;
-
let parent_id = fcx.tcx.hir().parent_id(id);
let parent = fcx.tcx.hir().get(parent_id);
if let Some(expr) = expression
@@ -1699,7 +1722,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
// label pointing out the cause for the type coercion will be wrong
// as prior return coercions would not be relevant (#57664).
let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) {
- pointing_at_return_type =
+ let pointing_at_return_type =
fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id);
if let (Some(cond_expr), true, false) = (
fcx.tcx.hir().get_if_cause(expr.hir_id),
@@ -1731,7 +1754,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
if let Some((fn_id, fn_decl, can_suggest)) = fn_decl {
if blk_id.is_none() {
- pointing_at_return_type |= fcx.suggest_missing_return_type(
+ fcx.suggest_missing_return_type(
&mut err,
&fn_decl,
expected,
@@ -1740,9 +1763,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
fn_id,
);
}
- if !pointing_at_return_type {
- fn_output = Some(&fn_decl.output); // `impl Trait` return type
- }
}
let parent_id = fcx.tcx.hir().get_parent_item(id);
@@ -1777,115 +1797,27 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
);
}
- if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) {
- self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output);
- }
-
err
}
- fn add_impl_trait_explanation<'a>(
- &self,
- err: &mut Diagnostic,
- cause: &ObligationCause<'tcx>,
- fcx: &FnCtxt<'a, 'tcx>,
- expected: Ty<'tcx>,
- sp: Span,
- fn_output: &hir::FnRetTy<'_>,
- ) {
- let return_sp = fn_output.span();
- err.span_label(return_sp, "expected because this return type...");
- err.span_label(
- sp,
- format!("...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected)),
- );
- let impl_trait_msg = "for information on `impl Trait`, see \
- <https://doc.rust-lang.org/book/ch10-02-traits.html\
- #returning-types-that-implement-traits>";
- let trait_obj_msg = "for information on trait objects, see \
- <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
- #using-trait-objects-that-allow-for-values-of-different-types>";
- err.note("to return `impl Trait`, all returned values must be of the same type");
- err.note(impl_trait_msg);
- let snippet = fcx
- .tcx
- .sess
- .source_map()
- .span_to_snippet(return_sp)
- .unwrap_or_else(|_| "dyn Trait".to_string());
- let mut snippet_iter = snippet.split_whitespace();
- let has_impl = snippet_iter.next().is_some_and(|s| s == "impl");
- // Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe.
- let mut is_object_safe = false;
- if let hir::FnRetTy::Return(ty) = fn_output
- // Get the return type.
- && let hir::TyKind::OpaqueDef(..) = ty.kind
- {
- let ty = fcx.astconv().ast_ty_to_ty( ty);
- // Get the `impl Trait`'s `DefId`.
- if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind()
- // Get the `impl Trait`'s `Item` so that we can get its trait bounds and
- // get the `Trait`'s `DefId`.
- && let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) =
- fcx.tcx.hir().expect_item(def_id.expect_local()).kind
- {
- // Are of this `impl Trait`'s traits object safe?
- is_object_safe = bounds.iter().all(|bound| {
- bound
- .trait_ref()
- .and_then(|t| t.trait_def_id())
- .is_some_and(|def_id| {
- fcx.tcx.check_is_object_safe(def_id)
- })
- })
- }
- };
- if has_impl {
- if is_object_safe {
- err.multipart_suggestion(
- "you could change the return type to be a boxed trait object",
- vec![
- (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
- (return_sp.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MachineApplicable,
- );
- let sugg = [sp, cause.span]
- .into_iter()
- .flat_map(|sp| {
- [
- (sp.shrink_to_lo(), "Box::new(".to_string()),
- (sp.shrink_to_hi(), ")".to_string()),
- ]
- .into_iter()
- })
- .collect::<Vec<_>>();
- err.multipart_suggestion(
- "if you change the return type to expect trait objects, box the returned \
- expressions",
- sugg,
- Applicability::MaybeIncorrect,
- );
- } else {
- err.help(format!(
- "if the trait `{}` were object safe, you could return a boxed trait object",
- &snippet[5..]
- ));
- }
- err.note(trait_obj_msg);
- }
- err.help("you could instead create a new `enum` with a variant for each returned type");
- }
-
- fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool {
- if let Some((_, fn_decl, _)) = fcx.get_fn_decl(blk_id)
- && let hir::FnRetTy::Return(ty) = fn_decl.output
- && let ty = fcx.astconv().ast_ty_to_ty( ty)
- && let ty::Dynamic(..) = ty.kind()
- {
- return true;
+ /// Checks whether the return type is unsized via an obligation, which makes
+ /// sure we consider `dyn Trait: Sized` where clauses, which are trivially
+ /// false but technically valid for typeck.
+ fn is_return_ty_definitely_unsized(&self, fcx: &FnCtxt<'_, 'tcx>) -> bool {
+ if let Some(sig) = fcx.body_fn_sig() {
+ !fcx.predicate_may_hold(&Obligation::new(
+ fcx.tcx,
+ ObligationCause::dummy(),
+ fcx.param_env,
+ ty::TraitRef::new(
+ fcx.tcx,
+ fcx.tcx.require_lang_item(hir::LangItem::Sized, None),
+ [sig.output()],
+ ),
+ ))
+ } else {
+ false
}
- false
}
pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index b50630e63..cc8198aab 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -3,7 +3,7 @@ use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::MultiSpan;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_hir::def::CtorKind;
+use rustc_hir::def::{CtorKind, Res};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{is_range_literal, Node};
@@ -83,6 +83,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
self.annotate_expected_due_to_let_ty(err, expr, error);
+
+ if self.is_destruct_assignment_desugaring(expr) {
+ return;
+ }
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_internal_mutation_in_method(err, expr, Some(expected), expr_ty);
@@ -91,6 +95,56 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.note_wrong_return_ty_due_to_generic_arg(err, expr, expr_ty);
}
+ /// Really hacky heuristic to remap an `assert_eq!` error to the user
+ /// expressions provided to the macro.
+ fn adjust_expr_for_assert_eq_macro(
+ &self,
+ found_expr: &mut &'tcx hir::Expr<'tcx>,
+ expected_expr: &mut Option<&'tcx hir::Expr<'tcx>>,
+ ) {
+ let Some(expected_expr) = expected_expr else { return; };
+
+ if !found_expr.span.eq_ctxt(expected_expr.span) {
+ return;
+ }
+
+ if !found_expr
+ .span
+ .ctxt()
+ .outer_expn_data()
+ .macro_def_id
+ .is_some_and(|def_id| self.tcx.is_diagnostic_item(sym::assert_eq_macro, def_id))
+ {
+ return;
+ }
+
+ let hir::ExprKind::Unary(
+ hir::UnOp::Deref,
+ hir::Expr { kind: hir::ExprKind::Path(found_path), .. },
+ ) = found_expr.kind else { return; };
+ let hir::ExprKind::Unary(
+ hir::UnOp::Deref,
+ hir::Expr { kind: hir::ExprKind::Path(expected_path), .. },
+ ) = expected_expr.kind else { return; };
+
+ for (path, name, idx, var) in [
+ (expected_path, "left_val", 0, expected_expr),
+ (found_path, "right_val", 1, found_expr),
+ ] {
+ if let hir::QPath::Resolved(_, path) = path
+ && let [segment] = path.segments
+ && segment.ident.name.as_str() == name
+ && let Res::Local(hir_id) = path.res
+ && let Some((_, hir::Node::Expr(match_expr))) = self.tcx.hir().parent_iter(hir_id).nth(2)
+ && let hir::ExprKind::Match(scrutinee, _, _) = match_expr.kind
+ && let hir::ExprKind::Tup(exprs) = scrutinee.kind
+ && let hir::ExprKind::AddrOf(_, _, macro_arg) = exprs[idx].kind
+ {
+ *var = macro_arg;
+ }
+ }
+ }
+
/// Requires that the two types unify, and prints an error message if
/// they don't.
pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
@@ -156,7 +210,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn demand_coerce(
&self,
- expr: &hir::Expr<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
@@ -177,10 +231,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
pub fn demand_coerce_diag(
&self,
- expr: &hir::Expr<'tcx>,
+ mut expr: &'tcx hir::Expr<'tcx>,
checked_ty: Ty<'tcx>,
expected: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
allow_two_phase: AllowTwoPhase,
) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
let expected = self.resolve_vars_with_obligations(expected);
@@ -190,6 +244,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Err(e) => e,
};
+ self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr);
+
self.set_tainted_by_errors(self.tcx.sess.delay_span_bug(
expr.span,
"`TypeError` when attempting coercion but no error emitted",
@@ -318,13 +374,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Fudge the receiver, so we can do new inference on it.
let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
let method = self
- .lookup_method(
+ .lookup_method_for_diagnostic(
possible_rcvr_ty,
segment,
DUMMY_SP,
call_expr,
binding,
- args,
)
.ok()?;
// Unify the method signature with our incompatible arg, to
@@ -383,14 +438,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
let Ok(method) =
- self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+ self.lookup_method_for_diagnostic(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
else {
continue;
};
let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
let ideal_method = self
- .lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+ .lookup_method_for_diagnostic(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr)
.ok()
.and_then(|method| {
let _ = self.at(&ObligationCause::dummy(), self.param_env)
@@ -1202,6 +1257,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
+ // Returns whether the given expression is a destruct assignment desugaring.
+ // For example, `(a, b) = (1, &2);`
+ // Here we try to find the pattern binding of the expression,
+ // `default_binding_modes` is false only for destruct assignment desugaring.
+ pub(crate) fn is_destruct_assignment_desugaring(&self, expr: &hir::Expr<'_>) -> bool {
+ if let hir::ExprKind::Path(hir::QPath::Resolved(
+ _,
+ hir::Path { res: hir::def::Res::Local(bind_hir_id), .. },
+ )) = expr.kind
+ {
+ let bind = self.tcx.hir().find(*bind_hir_id);
+ let parent = self.tcx.hir().find(self.tcx.hir().parent_id(*bind_hir_id));
+ if let Some(hir::Node::Pat(hir::Pat { kind: hir::PatKind::Binding(_, _hir_id, _, _), .. })) = bind &&
+ let Some(hir::Node::Pat(hir::Pat { default_binding_modes: false, .. })) = parent {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// This function is used to determine potential "simple" improvements or users' errors and
/// provide them useful help. For example:
///
@@ -1291,10 +1366,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// ```
let ref_ty = match mutability {
hir::Mutability::Mut => {
- self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, checked_ty)
+ Ty::new_mut_ref(self.tcx,self.tcx.lifetimes.re_static, checked_ty)
}
hir::Mutability::Not => {
- self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, checked_ty)
+ Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_static, checked_ty)
}
};
if self.can_coerce(ref_ty, expected) {
@@ -1392,6 +1467,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_,
&ty::Ref(_, checked, _),
) if self.can_sub(self.param_env, checked, expected) => {
+ let make_sugg = |start: Span, end: BytePos| {
+ // skip `(` for tuples such as `(c) = (&123)`.
+ // make sure we won't suggest like `(c) = 123)` which is incorrect.
+ let sp = sm.span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
+ .map_or(start, |s| s.shrink_to_hi());
+ Some((
+ vec![(sp.with_hi(end), String::new())],
+ "consider removing the borrow".to_string(),
+ Applicability::MachineApplicable,
+ true,
+ true,
+ ))
+ };
+
// We have `&T`, check if what was expected was `T`. If so,
// we may want to suggest removing a `&`.
if sm.is_imported(expr.span) {
@@ -1405,24 +1494,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.find(|&s| sp.contains(s))
&& sm.is_span_accessible(call_span)
{
- return Some((
- vec![(sp.with_hi(call_span.lo()), String::new())],
- "consider removing the borrow".to_string(),
- Applicability::MachineApplicable,
- true,
- true,
- ));
+ return make_sugg(sp, call_span.lo())
}
return None;
}
if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
- return Some((
- vec![(sp.with_hi(expr.span.lo()), String::new())],
- "consider removing the borrow".to_string(),
- Applicability::MachineApplicable,
- true,
- true,
- ));
+ return make_sugg(sp, expr.span.lo())
}
}
(
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 4222205c8..05906a4b9 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -2,7 +2,10 @@
use std::borrow::Cow;
use crate::fluent_generated as fluent;
-use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage};
+use rustc_errors::{
+ AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, MultiSpan,
+ SubdiagnosticMessage,
+};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::{
@@ -31,6 +34,24 @@ pub struct ReturnStmtOutsideOfFnBody {
pub encl_body_span: Option<Span>,
#[label(hir_typeck_encl_fn_label)]
pub encl_fn_span: Option<Span>,
+ pub statement_kind: ReturnLikeStatementKind,
+}
+
+pub enum ReturnLikeStatementKind {
+ Return,
+ Become,
+}
+
+impl IntoDiagnosticArg for ReturnLikeStatementKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ let kind = match self {
+ Self::Return => "return",
+ Self::Become => "become",
+ }
+ .into();
+
+ DiagnosticArgValue::Str(kind)
+ }
}
#[derive(Diagnostic)]
@@ -298,6 +319,17 @@ pub enum SuggestBoxing {
},
}
+#[derive(Subdiagnostic)]
+#[suggestion(
+ hir_typeck_suggest_ptr_null_mut,
+ applicability = "maybe-incorrect",
+ code = "core::ptr::null_mut()"
+)]
+pub struct SuggestPtrNullMut {
+ #[primary_span]
+ pub span: Span,
+}
+
#[derive(Diagnostic)]
#[diag(hir_typeck_no_associated_item, code = "E0599")]
pub struct NoAssociatedItem {
@@ -327,3 +359,19 @@ pub struct CtorIsPrivate {
pub span: Span,
pub def: String,
}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+ hir_typeck_convert_using_method,
+ applicability = "machine-applicable",
+ style = "verbose"
+)]
+pub struct SuggestConvertViaMethod<'tcx> {
+ #[suggestion_part(code = "{sugg}")]
+ pub span: Span,
+ #[suggestion_part(code = "")]
+ pub borrow_removal_span: Option<Span>,
+ pub sugg: &'static str,
+ pub expected: Ty<'tcx>,
+ pub found: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 19ff77d83..72b29f7b6 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -5,6 +5,7 @@
use crate::cast;
use crate::coercion::CoerceMany;
use crate::coercion::DynamicCoerceMany;
+use crate::errors::ReturnLikeStatementKind;
use crate::errors::TypeMismatchFruTypo;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use crate::errors::{
@@ -93,7 +94,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
target.to_owned()
} else {
- self.tcx().ty_error(reported)
+ Ty::new_error(self.tcx(), reported)
};
}
@@ -320,10 +321,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tcx.types.never
} else {
// There was an error; make type-check fail.
- tcx.ty_error_misc()
+ Ty::new_misc_error(tcx)
}
}
ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
+ ExprKind::Become(call) => self.check_expr_become(call, expr),
ExprKind::Let(let_expr) => self.check_expr_let(let_expr),
ExprKind::Loop(body, _, source, _) => {
self.check_expr_loop(body, source, expected, expr)
@@ -348,9 +350,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected),
ExprKind::Array(args) => self.check_expr_array(args, expected, expr),
- ExprKind::ConstBlock(ref anon_const) => {
- self.check_expr_const_block(anon_const, expected, expr)
- }
+ ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected, expr),
ExprKind::Repeat(element, ref count) => {
self.check_expr_repeat(element, count, expected, expr)
}
@@ -361,7 +361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected),
ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr),
ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src),
- hir::ExprKind::Err(guar) => tcx.ty_error(guar),
+ hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar),
}
}
@@ -380,7 +380,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
if !oprnd_t.references_error() {
- oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
+ oprnd_t = self.structurally_resolve_type(expr.span, oprnd_t);
match unop {
hir::UnOp::Deref => {
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
@@ -399,7 +399,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
}
- oprnd_t = tcx.ty_error(err.emit());
+ oprnd_t = Ty::new_error(tcx, err.emit());
}
}
hir::UnOp::Not => {
@@ -449,10 +449,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tm = ty::TypeAndMut { ty, mutbl };
match kind {
- _ if tm.ty.references_error() => self.tcx.ty_error_misc(),
+ _ if tm.ty.references_error() => Ty::new_misc_error(self.tcx),
hir::BorrowKind::Raw => {
self.check_named_place_expr(oprnd);
- self.tcx.mk_ptr(tm)
+ Ty::new_ptr(self.tcx, tm)
}
hir::BorrowKind::Ref => {
// Note: at this point, we cannot say what the best lifetime
@@ -470,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// whose address was taken can actually be made to live as long
// as it needs to live.
let region = self.next_region_var(infer::AddrOfRegion(expr.span));
- self.tcx.mk_ref(region, tm)
+ Ty::new_ref(self.tcx, region, tm)
}
}
}
@@ -528,11 +528,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let e =
self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
- tcx.ty_error(e)
+ Ty::new_error(tcx, e)
}
Res::Def(DefKind::Variant, _) => {
let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, "E0533", "value");
- tcx.ty_error(e)
+ Ty::new_error(tcx, e)
}
_ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0,
};
@@ -620,7 +620,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()),
None => {
// Avoid ICE when `break` is inside a closure (#65383).
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(
+ tcx,
expr.span,
"break was outside loop, but no error was emitted",
);
@@ -631,7 +632,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the loop context is not a `loop { }`, then break with
// a value is illegal, and `opt_coerce_to` will be `None`.
// Just set expectation to error in that case.
- let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error_misc());
+ let coerce_to = opt_coerce_to.unwrap_or_else(|| Ty::new_misc_error(tcx));
// Recurse without `enclosing_breakables` borrowed.
e_ty = self.check_expr_with_hint(e, coerce_to);
@@ -639,7 +640,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
// Otherwise, this is a break *without* a value. That's
// always legal, and is equivalent to `break ()`.
- e_ty = tcx.mk_unit();
+ e_ty = Ty::new_unit(tcx);
cause = self.misc(expr.span);
}
@@ -649,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else {
// Avoid ICE when `break` is inside a closure (#65383).
- return tcx.ty_error_with_message(
+ return Ty::new_error_with_message(tcx,
expr.span,
"break was outside loop, but no error was emitted",
);
@@ -707,7 +708,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// this can only happen if the `break` was not
// inside a loop at all, which is caught by the
// loop-checking pass.
- let err = self.tcx.ty_error_with_message(
+ let err = Ty::new_error_with_message(
+ self.tcx,
expr.span,
"break was outside loop, but no error was emitted",
);
@@ -737,47 +739,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
if self.ret_coercion.is_none() {
- let mut err = ReturnStmtOutsideOfFnBody {
- span: expr.span,
- encl_body_span: None,
- encl_fn_span: None,
- };
-
- let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
-
- if let Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Fn(..),
- span: encl_fn_span,
- ..
- }))
- | Some(hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
- span: encl_fn_span,
- ..
- }))
- | Some(hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(..),
- span: encl_fn_span,
- ..
- })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
- {
- // We are inside a function body, so reporting "return statement
- // outside of function body" needs an explanation.
-
- let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
-
- // If this didn't hold, we would not have to report an error in
- // the first place.
- assert_ne!(encl_item_id.def_id, encl_body_owner_id);
-
- let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
- let encl_body = self.tcx.hir().body(encl_body_id);
-
- err.encl_body_span = Some(encl_body.value.span);
- err.encl_fn_span = Some(*encl_fn_span);
- }
-
- self.tcx.sess.emit_err(err);
+ self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
if let Some(e) = expr_opt {
// We still have to type-check `e` (issue #86188), but calling
@@ -817,6 +779,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx.types.never
}
+ fn check_expr_become(
+ &self,
+ call: &'tcx hir::Expr<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> Ty<'tcx> {
+ match &self.ret_coercion {
+ Some(ret_coercion) => {
+ let ret_ty = ret_coercion.borrow().expected_ty();
+ let call_expr_ty = self.check_expr_with_hint(call, ret_ty);
+
+ // N.B. don't coerce here, as tail calls can't support most/all coercions
+ // FIXME(explicit_tail_calls): add a diagnostic note that `become` doesn't allow coercions
+ self.demand_suptype(expr.span, ret_ty, call_expr_ty);
+ }
+ None => {
+ self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Become);
+
+ // Fallback to simply type checking `call` without hint/demanding the right types.
+ // Best effort to highlight more errors.
+ self.check_expr(call);
+ }
+ }
+
+ self.tcx.types.never
+ }
+
+ /// Check an expression that _is being returned_.
+ /// For example, this is called with `return_expr: $expr` when `return $expr`
+ /// is encountered.
+ ///
+ /// Note that this function must only be called in function bodies.
+ ///
/// `explicit_return` is `true` if we're checking an explicit `return expr`,
/// and `false` if we're checking a trailing expression.
pub(super) fn check_return_expr(
@@ -833,10 +827,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut span = return_expr.span;
// Use the span of the trailing expression for our cause,
// not the span of the entire function
- if !explicit_return {
- if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr {
+ if !explicit_return
+ && let ExprKind::Block(body, _) = return_expr.kind
+ && let Some(last_expr) = body.expr
+ {
span = last_expr.span;
- }
}
ret_coercion.borrow_mut().coerce(
self,
@@ -856,6 +851,55 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ /// Emit an error because `return` or `become` is used outside of a function body.
+ ///
+ /// `expr` is the `return` (`become`) "statement", `kind` is the kind of the statement
+ /// either `Return` or `Become`.
+ fn emit_return_outside_of_fn_body(&self, expr: &hir::Expr<'_>, kind: ReturnLikeStatementKind) {
+ let mut err = ReturnStmtOutsideOfFnBody {
+ span: expr.span,
+ encl_body_span: None,
+ encl_fn_span: None,
+ statement_kind: kind,
+ };
+
+ let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id);
+
+ if let Some(hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Fn(..),
+ span: encl_fn_span,
+ ..
+ }))
+ | Some(hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
+ span: encl_fn_span,
+ ..
+ }))
+ | Some(hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(..),
+ span: encl_fn_span,
+ ..
+ })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id)
+ {
+ // We are inside a function body, so reporting "return statement
+ // outside of function body" needs an explanation.
+
+ let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id);
+
+ // If this didn't hold, we would not have to report an error in
+ // the first place.
+ assert_ne!(encl_item_id.def_id, encl_body_owner_id);
+
+ let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id);
+ let encl_body = self.tcx.hir().body(encl_body_id);
+
+ err.encl_body_span = Some(encl_body.value.span);
+ err.encl_fn_span = Some(*encl_fn_span);
+ }
+
+ self.tcx.sess.emit_err(err);
+ }
+
fn point_at_return_for_opaque_ty_error(
&self,
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
@@ -1030,7 +1074,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
let result_ty = coerce.complete(self);
- if let Err(guar) = cond_ty.error_reported() { self.tcx.ty_error(guar) } else { result_ty }
+ if let Err(guar) = cond_ty.error_reported() {
+ Ty::new_error(self.tcx, guar)
+ } else {
+ result_ty
+ }
}
/// Type check assignment expression `expr` of form `lhs = rhs`.
@@ -1048,7 +1096,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// The expected type is `bool` but this will result in `()` so we can reasonably
// say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
// The likely cause of this is `if foo = bar { .. }`.
- let actual_ty = self.tcx.mk_unit();
+ let actual_ty = Ty::new_unit(self.tcx);
let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
let lhs_ty = self.check_expr(&lhs);
let rhs_ty = self.check_expr(&rhs);
@@ -1106,7 +1154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the assignment expression itself is ill-formed, don't
// bother emitting another error
let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error());
- return self.tcx.ty_error(reported);
+ return Ty::new_error(self.tcx, reported);
}
let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
@@ -1153,9 +1201,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized);
if let Err(guar) = (lhs_ty, rhs_ty).error_reported() {
- self.tcx.ty_error(guar)
+ Ty::new_error(self.tcx, guar)
} else {
- self.tcx.mk_unit()
+ Ty::new_unit(self.tcx)
}
}
@@ -1210,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// [1]
self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break");
}
- ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit())
+ ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx))
}
/// Checks a method call.
@@ -1224,14 +1272,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let rcvr_t = self.check_expr(&rcvr);
// no need to check for bot/err -- callee does that
- let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t);
+ let rcvr_t = self.structurally_resolve_type(rcvr.span, rcvr_t);
let span = segment.ident.span;
let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
Ok(method) => {
// We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to
- // trigger this codepath causing `structurally_resolved_type` to emit an error.
+ // trigger this codepath causing `structurally_resolve_type` to emit an error.
+ self.enforce_context_effects(expr.hir_id, expr.span, method.def_id, method.substs);
self.write_method_call(expr.hir_id, method);
Ok(method)
}
@@ -1273,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Eagerly check for some obvious errors.
if let Err(guar) = (t_expr, t_cast).error_reported() {
- self.tcx.ty_error(guar)
+ Ty::new_error(self.tcx, guar)
} else {
// Defer other checks until we're done type checking.
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
@@ -1294,7 +1343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
deferred_cast_checks.push(cast_check);
t_cast
}
- Err(guar) => self.tcx.ty_error(guar),
+ Err(guar) => Ty::new_error(self.tcx, guar),
}
}
}
@@ -1334,7 +1383,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let array_len = args.len() as u64;
self.suggest_array_len(expr, array_len);
- self.tcx.mk_array(element_ty, array_len)
+ Ty::new_array(self.tcx, element_ty, array_len)
}
fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) {
@@ -1368,20 +1417,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_expr_const_block(
&self,
- anon_const: &'tcx hir::AnonConst,
+ block: &'tcx hir::ConstBlock,
expected: Expectation<'tcx>,
_expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
- let body = self.tcx.hir().body(anon_const.body);
+ let body = self.tcx.hir().body(block.body);
// Create a new function context.
- let def_id = anon_const.def_id;
+ let def_id = block.def_id;
let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id);
crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
let ty = fcx.check_expr_with_expectation(&body.value, expected);
fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized);
- fcx.write_ty(anon_const.hir_id, ty);
+ fcx.write_ty(block.hir_id, ty);
ty
}
@@ -1422,18 +1471,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
if let Err(guar) = element_ty.error_reported() {
- return tcx.ty_error(guar);
+ return Ty::new_error(tcx, guar);
}
self.check_repeat_element_needs_copy_bound(element, count, element_ty);
self.register_wf_obligation(
- tcx.mk_array_with_const_len(t, count).into(),
+ Ty::new_array_with_const_len(tcx, t, count).into(),
expr.span,
traits::WellFormed(None),
);
- tcx.mk_array_with_const_len(t, count)
+ Ty::new_array_with_const_len(tcx, t, count)
}
fn check_repeat_element_needs_copy_bound(
@@ -1496,9 +1545,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
_ => self.check_expr_with_expectation(&e, NoExpectation),
});
- let tuple = self.tcx.mk_tup_from_iter(elt_ts_iter);
+ let tuple = Ty::new_tup_from_iter(self.tcx, elt_ts_iter);
if let Err(guar) = tuple.error_reported() {
- self.tcx.ty_error(guar)
+ Ty::new_error(self.tcx, guar)
} else {
self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized);
tuple
@@ -1518,7 +1567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(data) => data,
Err(guar) => {
self.check_struct_fields_on_error(fields, base_expr);
- return self.tcx.ty_error(guar);
+ return Ty::new_error(self.tcx, guar);
}
};
@@ -1617,7 +1666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
};
- tcx.ty_error(guar)
+ Ty::new_error(tcx, guar)
};
// Make sure to give a type to the field even if there's
@@ -1729,7 +1778,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
// This is important to allow coercions to happen in
// `other_struct` itself. See `coerce-in-base-expr.rs`.
- let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs);
+ let fresh_base_ty = Ty::new_adt(self.tcx, *adt, fresh_substs);
self.check_expr_has_type_or_error(
base_expr,
self.resolve_vars_if_possible(fresh_base_ty),
@@ -2083,13 +2132,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
},
_ => {
// prevent all specified fields from being suggested
- let skip_fields = skip_fields.iter().map(|x| x.ident.name);
- if let Some(field_name) = self.suggest_field_name(
- variant,
- field.ident.name,
- skip_fields.collect(),
- expr_span,
- ) {
+ let skip_fields: Vec<_> = skip_fields.iter().map(|x| x.ident.name).collect();
+ if let Some(field_name) =
+ self.suggest_field_name(variant, field.ident.name, &skip_fields, expr_span)
+ {
err.span_suggestion(
field.ident.span,
"a field with a similar name exists",
@@ -2110,9 +2156,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("`{ty}` does not have this field"),
);
}
- let available_field_names =
+ let mut available_field_names =
self.available_field_names(variant, expr_span);
- if !available_field_names.is_empty() {
+ available_field_names
+ .retain(|name| skip_fields.iter().all(|skip| name != skip));
+ if available_field_names.is_empty() {
+ err.note("all struct fields are already assigned");
+ } else {
err.note(format!(
"available fields are: {}",
self.name_series_display(available_field_names)
@@ -2132,7 +2182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
variant: &'tcx ty::VariantDef,
field: Symbol,
- skip: Vec<Symbol>,
+ skip: &[Symbol],
// The span where stability will be checked
span: Span,
) -> Option<Symbol> {
@@ -2209,7 +2259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
let base_ty = self.check_expr(base);
- let base_ty = self.structurally_resolved_type(base.span, base_ty);
+ let base_ty = self.structurally_resolve_type(base.span, base_ty);
let mut private_candidate = None;
let mut autoderef = self.autoderef(expr.span, base_ty);
while let Some((deref_base_ty, _)) = autoderef.next() {
@@ -2257,7 +2307,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => {}
}
}
- self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+ self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
if let Some((adjustments, did)) = private_candidate {
// (#90483) apply adjustments to avoid ExprUseVisitor from
@@ -2270,7 +2320,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
did,
expected.only_has_type(self),
);
- return self.tcx().ty_error(guar);
+ return Ty::new_error(self.tcx(), guar);
}
let guar = if field.name == kw::Empty {
@@ -2356,7 +2406,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit()
};
- self.tcx().ty_error(guar)
+ Ty::new_error(self.tcx(), guar)
}
fn suggest_await_on_field_access(
@@ -2584,7 +2634,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
access_span: Span,
) {
if let Some(suggested_field_name) =
- self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span)
+ self.suggest_field_name(def.non_enum_variant(), field.name, &[], access_span)
{
err.span_suggestion(
field.span,
@@ -2814,7 +2864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if idx_t.references_error() {
idx_t
} else {
- let base_t = self.structurally_resolved_type(base.span, base_t);
+ let base_t = self.structurally_resolve_type(base.span, base_t);
match self.lookup_indexing(expr, base, base_t, idx, idx_t) {
Some((index_ty, element_ty)) => {
// two-phase not needed because index_ty is never mutable
@@ -2872,8 +2922,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
}
+
+ if base_t.is_unsafe_ptr() && idx_t.is_integral() {
+ err.multipart_suggestion(
+ "consider using `wrapping_add` or `add` for indexing into raw pointer",
+ vec![
+ (base.span.between(idx.span), ".wrapping_add(".to_owned()),
+ (
+ idx.span.shrink_to_hi().until(expr.span.shrink_to_hi()),
+ ")".to_owned(),
+ ),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+
let reported = err.emit();
- self.tcx.ty_error(reported)
+ Ty::new_error(self.tcx, reported)
}
}
}
@@ -2904,7 +2969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
self.commit_if_ok(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
let impl_substs = self.fresh_substs_for_item(base_expr.span, impl_def_id);
let impl_trait_ref =
self.tcx.impl_trait_ref(impl_def_id).unwrap().subst(self.tcx, impl_substs);
@@ -2947,7 +3012,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let element_ty = ocx.normalize(
&cause,
self.param_env,
- self.tcx.mk_projection(index_trait_output_def_id, impl_trait_ref.substs),
+ Ty::new_projection(self.tcx, index_trait_output_def_id, impl_trait_ref.substs),
);
let errors = ocx.select_where_possible();
@@ -2969,7 +3034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
for error in errors {
match error.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate))
if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => {
}
_ => continue,
@@ -2995,14 +3060,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// information. Hence, we check the source of the yield expression here and check its
// value's type against `()` (this check should always hold).
None if src.is_await() => {
- self.check_expr_coercible_to_type(&value, self.tcx.mk_unit(), None);
- self.tcx.mk_unit()
+ self.check_expr_coercible_to_type(&value, Ty::new_unit(self.tcx), None);
+ Ty::new_unit(self.tcx)
}
_ => {
self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span });
// Avoid expressions without types during writeback (#78653).
self.check_expr(value);
- self.tcx.mk_unit()
+ Ty::new_unit(self.tcx)
}
}
}
@@ -3026,14 +3091,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
- let ty = self.structurally_resolved_type(expr.span, ty);
+ let ty = self.structurally_resolve_type(expr.span, ty);
match *ty.kind() {
ty::FnDef(..) => {
- let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
+ let fnptr_ty = Ty::new_fn_ptr(self.tcx, ty.fn_sig(self.tcx));
self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No);
}
ty::Ref(_, base_ty, mutbl) => {
- let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl });
+ let ptr_ty = Ty::new_ptr(self.tcx, ty::TypeAndMut { ty: base_ty, mutbl });
self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No);
}
_ => {}
@@ -3068,7 +3133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if asm.options.contains(ast::InlineAsmOptions::NORETURN) {
self.tcx.types.never
} else {
- self.tcx.mk_unit()
+ Ty::new_unit(self.tcx)
}
}
@@ -3084,7 +3149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut current_container = container;
for &field in fields {
- let container = self.structurally_resolved_type(expr.span, current_container);
+ let container = self.structurally_resolve_type(expr.span, current_container);
match container.kind() {
ty::Adt(container_def, substs) if !container_def.is_enum() => {
@@ -3117,16 +3182,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
ty::Tuple(tys) => {
- let fstr = field.as_str();
-
- if let Ok(index) = fstr.parse::<usize>() {
- if fstr == index.to_string() {
- if let Some(&field_ty) = tys.get(index) {
- field_indices.push(index.into());
- current_container = field_ty;
+ if let Ok(index) = field.as_str().parse::<usize>()
+ && field.name == sym::integer(index)
+ {
+ for ty in tys.iter().take(index + 1) {
+ self.require_type_is_sized(ty, expr.span, traits::MiscObligation);
+ }
+ if let Some(&field_ty) = tys.get(index) {
+ field_indices.push(index.into());
+ current_container = field_ty;
- continue;
- }
+ continue;
}
}
}
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index e14e8ac2c..0d2e0602e 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -326,6 +326,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
}
}
+ hir::ExprKind::Become(call) => {
+ self.consume_expr(call);
+ }
+
hir::ExprKind::Assign(lhs, rhs, _) => {
self.mutate_expr(lhs);
self.consume_expr(rhs);
@@ -443,7 +447,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
if matches!((lhs, wild, rhs), (&[], Some(_), &[]))
// Arrays have a statically known size, so
// there is no need to read their length
- || discr_place.place.base_ty.is_array()
+ || place.place.ty().peel_refs().is_array()
{
} else {
needs_to_be_read = true;
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index b7ae621c6..a76db6e73 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -104,7 +104,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
// type, `?T` is not considered unsolved, but `?I` is. The
// same is true for float variables.)
let fallback = match ty.kind() {
- _ if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e),
+ _ if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx,e),
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
_ => match diverging_fallback.get(&ty) {
@@ -287,7 +287,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
let mut diverging_fallback = FxHashMap::default();
diverging_fallback.reserve(diverging_vids.len());
for &diverging_vid in &diverging_vids {
- let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
+ let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
let root_vid = self.root_var(diverging_vid);
let can_reach_non_diverging = coercion_graph
.depth_first_search(root_vid)
@@ -334,7 +334,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
} else {
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
- diverging_fallback.insert(diverging_ty, self.tcx.mk_diverging_default());
+ diverging_fallback.insert(diverging_ty, Ty::new_diverging_default(self.tcx));
}
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 557950338..9a80a9c93 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -2,7 +2,7 @@ use crate::callee::{self, DeferredCallResolution};
use crate::errors::CtorIsPrivate;
use crate::method::{self, MethodCallee, SelfSource};
use crate::rvalue_scopes;
-use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
+use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, RawTy};
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
@@ -83,6 +83,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// version (resolve_vars_if_possible), this version will
/// also select obligations if it seems useful, in an effort
/// to get more type information.
+ // FIXME(-Ztrait-solver=next): A lot of the calls to this method should
+ // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead.
pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {})
}
@@ -135,7 +137,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("{:p}", self)
}
- pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> {
+ pub fn local_ty(&self, span: Span, nid: hir::HirId) -> Ty<'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))
})
@@ -451,7 +453,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> {
match self.typeck_results.borrow().node_types().get(id) {
Some(&t) => t,
- None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e),
+ None if let Some(e) = self.tainted_by_errors() => Ty::new_error(self.tcx,e),
None => {
bug!(
"no type for node {} in fcx {}",
@@ -465,7 +467,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> {
match self.typeck_results.borrow().node_types().get(id) {
Some(&t) => Some(t),
- None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error(e)),
+ None if let Some(e) = self.tainted_by_errors() => Some(Ty::new_error(self.tcx,e)),
None => None,
}
}
@@ -483,7 +485,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
cause,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))),
));
}
@@ -556,7 +558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
);
- let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs);
+ let witness = Ty::new_generator_witness_mir(self.tcx, expr_def_id.to_def_id(), substs);
// Unify `interior` with `witness` and collect all the resulting obligations.
let span = self.tcx.hir().body(body_id).value.span;
@@ -647,7 +649,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map(
move |obligation| match &obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(data))
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data))
if self.self_type_matches_expected_vid(
data.projection_ty.self_ty(),
ty_var_root,
@@ -655,23 +657,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{
Some(obligation)
}
- ty::PredicateKind::Clause(ty::Clause::Trait(data))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data))
if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) =>
{
Some(obligation)
}
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::WellFormed(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
// N.B., this predicate is created by breaking down a
// `ClosureType: FnFoo()` predicate, where
@@ -683,7 +685,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// inference variable.
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ => None,
},
)
}
@@ -692,7 +694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let sized_did = self.tcx.lang_items().sized_trait();
self.obligations_for_self_ty(self_ty).any(|obligation| {
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
Some(data.def_id()) == sized_did
}
_ => false,
@@ -701,7 +703,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
- let ty_error = self.tcx.ty_error_misc();
+ let ty_error = Ty::new_misc_error(self.tcx);
vec![ty_error; len]
}
@@ -746,7 +748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expect_args = self
.fudge_inference_if_ok(|| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
// Attempt to apply a subtyping relationship between the formal
// return type (likely containing type variables if the function
@@ -1152,7 +1154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
if let Res::Local(hid) = res {
- let ty = self.local_ty(span, hid).decl_ty;
+ let ty = self.local_ty(span, hid);
let ty = self.normalize(span, ty);
self.write_ty(hir_id, ty);
return (ty, res);
@@ -1240,7 +1242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
let reported = err.emit();
- return (tcx.ty_error(reported), res);
+ return (Ty::new_error(tcx, reported), res);
}
}
} else {
@@ -1386,7 +1388,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// the referenced item.
let ty = tcx.type_of(def_id);
assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.0.has_escaping_bound_vars());
+ assert!(!ty.skip_binder().has_escaping_bound_vars());
let ty_substituted = self.normalize(span, ty.subst(tcx, substs));
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
@@ -1465,33 +1467,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
- /// Resolves `typ` by a single level if `typ` is a type variable.
+ /// Try to resolve `ty` to a structural type, normalizing aliases.
///
- /// When the new solver is enabled, this will also attempt to normalize
- /// the type if it's a projection (note that it will not deeply normalize
- /// projections within the type, just the outermost layer of the type).
- ///
- /// 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 mut ty = self.resolve_vars_with_obligations(ty);
+ /// In case there is still ambiguity, the returned type may be an inference
+ /// variable. This is different from `structurally_resolve_type` which errors
+ /// in this case.
+ pub fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let ty = self.resolve_vars_with_obligations(ty);
- if self.tcx.trait_solver_next()
+ if self.next_trait_solver()
&& let ty::Alias(ty::Projection, _) = ty.kind()
{
match self
.at(&self.misc(sp), self.param_env)
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
{
- Ok(normalized_ty) => {
- ty = normalized_ty;
- },
+ Ok(normalized_ty) => normalized_ty,
Err(errors) => {
let guar = self.err_ctxt().report_fulfillment_errors(&errors);
- return self.tcx.ty_error(guar);
+ return Ty::new_error(self.tcx,guar);
}
}
- }
+ } else {
+ ty
+ }
+ }
+
+ /// Resolves `ty` by a single level if `ty` is a type variable.
+ ///
+ /// When the new solver is enabled, this will also attempt to normalize
+ /// the type if it's a projection (note that it will not deeply normalize
+ /// projections within the type, just the outermost layer of the type).
+ ///
+ /// If no resolution is possible, then an error is reported.
+ /// Numeric inference variables may be left unresolved.
+ pub fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let ty = self.try_structurally_resolve_type(sp, ty);
if !ty.is_ty_var() {
ty
@@ -1501,7 +1512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true)
.emit()
});
- let err = self.tcx.ty_error(e);
+ let err = Ty::new_error(self.tcx, e);
self.demand_suptype(sp, err, ty);
err
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
index 3efdab534..ed9bb4945 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs
@@ -25,14 +25,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let generics = self.tcx.generics_of(def_id);
let predicate_substs = match unsubstituted_pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs.to_vec(),
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
- pred.projection_ty.substs.to_vec()
- }
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(arg, ty)) => {
+ ty::ClauseKind::Trait(pred) => pred.trait_ref.substs.to_vec(),
+ ty::ClauseKind::Projection(pred) => pred.projection_ty.substs.to_vec(),
+ ty::ClauseKind::ConstArgHasType(arg, ty) => {
vec![ty.into(), arg.into()]
}
- ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()],
+ ty::ClauseKind::ConstEvaluatable(e) => vec![e.into()],
_ => return false,
};
@@ -256,7 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
type BreakTy = ty::GenericArg<'tcx>;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
if let Some(origin) = self.0.type_var_origin(ty)
- && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) =
+ && let rustc_infer::infer::type_variable::TypeVariableOriginKind::TypeParameterDefinition(_, def_id) =
origin.kind
&& let generics = self.0.tcx.generics_of(self.1)
&& let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id)
@@ -510,11 +508,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things.
return Err(expr);
}
- let relevant_broken_predicate: ty::PredicateKind<'tcx> =
- impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder();
- match relevant_broken_predicate {
- ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => {
+ match impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder() {
+ ty::ClauseKind::Trait(broken_trait) => {
// ...
self.blame_specific_part_of_expr_corresponding_to_generic_param(
broken_trait.trait_ref.self_ty().into(),
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
index d45e3d395..7aadb95d9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs
@@ -1,7 +1,7 @@
-use std::cmp;
-
+use core::cmp::Ordering;
use rustc_index::IndexVec;
use rustc_middle::ty::error::TypeError;
+use std::cmp;
rustc_index::newtype_index! {
#[debug_format = "ExpectedIdx({})"]
@@ -34,14 +34,14 @@ enum Issue {
Permutation(Vec<Option<usize>>),
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum Compatibility<'tcx> {
Compatible,
Incompatible(Option<TypeError<'tcx>>),
}
/// Similar to `Issue`, but contains some extra information
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Error<'tcx> {
/// The provided argument is the invalid type for the expected input
Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
@@ -55,6 +55,34 @@ pub(crate) enum Error<'tcx> {
Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
}
+impl Ord for Error<'_> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let key = |error: &Error<'_>| -> usize {
+ match error {
+ Error::Invalid(..) => 0,
+ Error::Extra(_) => 1,
+ Error::Missing(_) => 2,
+ Error::Swap(..) => 3,
+ Error::Permutation(..) => 4,
+ }
+ };
+ match (self, other) {
+ (Error::Invalid(a, _, _), Error::Invalid(b, _, _)) => a.cmp(b),
+ (Error::Extra(a), Error::Extra(b)) => a.cmp(b),
+ (Error::Missing(a), Error::Missing(b)) => a.cmp(b),
+ (Error::Swap(a, b, ..), Error::Swap(c, d, ..)) => a.cmp(c).then(b.cmp(d)),
+ (Error::Permutation(a), Error::Permutation(b)) => a.cmp(b),
+ _ => key(self).cmp(&key(other)),
+ }
+ }
+}
+
+impl PartialOrd for Error<'_> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
pub(crate) struct ArgMatrix<'tcx> {
/// Maps the indices in the `compatibility_matrix` rows to the indices of
/// the *user provided* inputs
@@ -177,7 +205,7 @@ impl<'tcx> ArgMatrix<'tcx> {
// If an argument is unsatisfied, and the input in its position is useless
// then the most likely explanation is that we just got the types wrong
(true, true, true, true) => return Some(Issue::Invalid(i)),
- // Otherwise, if an input is useless, then indicate that this is an extra argument
+ // Otherwise, if an input is useless then indicate that this is an extra input
(true, _, true, _) => return Some(Issue::Extra(i)),
// Otherwise, if an argument is unsatisfiable, indicate that it's missing
(_, true, _, true) => return Some(Issue::Missing(i)),
@@ -376,6 +404,9 @@ impl<'tcx> ArgMatrix<'tcx> {
};
}
+ // sort errors with same type by the order they appear in the source
+ // so that suggestion will be handled properly, see #112507
+ errors.sort();
return (errors, matched_inputs);
}
}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index eba5c829e..41f5fafe7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1,12 +1,12 @@
use crate::coercion::CoerceMany;
+use crate::errors::SuggestPtrNullMut;
use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
use crate::gather_locals::Declaration;
use crate::method::MethodCallee;
use crate::TupleArgumentsFlag::*;
use crate::{errors, Expectation::*};
use crate::{
- struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy,
- TupleArgumentsFlag,
+ struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag,
};
use rustc_ast as ast;
use rustc_data_structures::fx::FxIndexSet;
@@ -73,7 +73,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
let ty = self.resolve_vars_if_possible(ty);
if ty.has_non_region_infer() {
- self.tcx.ty_error_misc()
+ Ty::new_misc_error(self.tcx)
} else {
self.tcx.erase_regions(ty)
}
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let err_inputs = match tuple_arguments {
DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.mk_tup(&err_inputs)],
+ TupleArguments => vec![Ty::new_tup(self.tcx, &err_inputs)],
};
self.check_argument_types(
@@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tuple_arguments,
method.ok().map(|method| method.def_id),
);
- return self.tcx.ty_error_misc();
+ return Ty::new_misc_error(self.tcx);
}
let method = method.unwrap();
@@ -184,7 +184,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
+ let tuple_type = self.structurally_resolve_type(call_span, formal_input_tys[0]);
match tuple_type.kind() {
// We expected a tuple and got a tuple
ty::Tuple(arg_types) => {
@@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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);
+ let arg_ty = self.structurally_resolve_type(arg.span, arg_ty);
match arg_ty.kind() {
ty::Float(ty::FloatTy::F32) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
@@ -424,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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 = Ty::new_fn_ptr(self.tcx, 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());
}
@@ -539,7 +539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.typeck_results
.borrow()
.expr_ty_adjusted_opt(*expr)
- .unwrap_or_else(|| tcx.ty_error_misc());
+ .unwrap_or_else(|| Ty::new_misc_error(tcx));
(self.resolve_vars_if_possible(ty), normalize_span(expr.span))
})
.collect();
@@ -648,7 +648,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
{
// Wrap up the N provided arguments starting at this position in a tuple.
- let provided_as_tuple = tcx.mk_tup_from_iter(
+ let provided_as_tuple = Ty::new_tup_from_iter(tcx,
provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
);
@@ -752,20 +752,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
- errors.drain_filter(|error| {
+ errors.retain(|error| {
let Error::Invalid(
provided_idx,
expected_idx,
Compatibility::Incompatible(Some(e)),
- ) = error else { return false };
+ ) = error else { return true };
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
let trace =
mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
- return true;
+ return false;
}
- false
+ true
});
// We're done if we found errors, but we already emitted them.
@@ -814,6 +814,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
+ self.suggest_ptr_null_mut(
+ expected_ty,
+ provided_ty,
+ provided_args[*provided_idx],
+ &mut err,
+ );
+
// Call out where the function is defined
self.label_fn_like(
&mut err,
@@ -947,9 +954,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// - f(0, 1,)
// + f()
if only_extras_so_far
- && errors
+ && !errors
.peek()
- .map_or(true, |next_error| !matches!(next_error, Error::Extra(_)))
+ .is_some_and(|next_error| matches!(next_error, Error::Extra(_)))
{
let next = provided_arg_tys
.get(arg_idx + 1)
@@ -1271,6 +1278,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.emit();
}
+ fn suggest_ptr_null_mut(
+ &self,
+ expected_ty: Ty<'tcx>,
+ provided_ty: Ty<'tcx>,
+ arg: &hir::Expr<'tcx>,
+ err: &mut rustc_errors::DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) {
+ if let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Mut, .. }) = expected_ty.kind()
+ && let ty::RawPtr(ty::TypeAndMut { mutbl: hir::Mutability::Not, .. }) = provided_ty.kind()
+ && let hir::ExprKind::Call(callee, _) = arg.kind
+ && let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = callee.kind
+ && let Res::Def(_, def_id) = path.res
+ && self.tcx.get_diagnostic_item(sym::ptr_null) == Some(def_id)
+ {
+ // The user provided `ptr::null()`, but the function expects
+ // `ptr::null_mut()`.
+ err.subdiagnostic(SuggestPtrNullMut {
+ span: arg.span
+ });
+ }
+ }
+
// AST fragment checking
pub(in super::super) fn check_lit(
&self,
@@ -1280,14 +1309,16 @@ impl<'a, 'tcx> FnCtxt<'a, '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::Str(..) => Ty::new_static_str(tcx),
+ ast::LitKind::ByteStr(ref v, _) => Ty::new_imm_ref(
+ tcx,
+ tcx.lifetimes.re_static,
+ Ty::new_array(tcx, 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(ty::int_ty(t)),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
+ ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => Ty::new_int(tcx, ty::int_ty(t)),
+ ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => Ty::new_uint(tcx, ty::uint_ty(t)),
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
ty::Int(_) | ty::Uint(_) => Some(ty),
@@ -1299,7 +1330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_ty.unwrap_or_else(|| self.next_int_var())
}
ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
- tcx.mk_mach_float(ty::float_ty(t))
+ Ty::new_float(tcx, ty::float_ty(t))
}
ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
@@ -1309,12 +1340,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_ty.unwrap_or_else(|| self.next_float_var())
}
ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::CStr(_, _) => tcx.mk_imm_ref(
+ ast::LitKind::CStr(_, _) => Ty::new_imm_ref(
+ tcx,
tcx.lifetimes.re_static,
tcx.type_of(tcx.require_lang_item(hir::LangItem::CStr, Some(lit.span)))
.skip_binder(),
),
- ast::LitKind::Err => tcx.ty_error_misc(),
+ ast::LitKind::Err => Ty::new_misc_error(tcx),
}
}
@@ -1393,7 +1425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// See #44848.
let ref_bindings = pat.contains_explicit_ref_binding();
- let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
+ let local_ty = self.local_ty(init.span, hir_id);
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
@@ -1404,7 +1436,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// 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);
+ if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
+ self.emit_type_mismatch_suggestions(
+ &mut diag,
+ init.peel_drop_temps(),
+ init_ty,
+ local_ty,
+ None,
+ None,
+ );
+ diag.emit();
+ }
init_ty
} else {
self.check_expr_coercible_to_type(init, local_ty, None)
@@ -1413,7 +1455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
// Determine and write the type which we'll check the pattern against.
- let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
+ let decl_ty = self.local_ty(decl.span, decl.hir_id);
self.write_ty(decl.hir_id, decl_ty);
// Type check the initializer.
@@ -1474,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
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.check_expr_has_type_or_error(&expr, Ty::new_unit(self.tcx), |err| {
if expr.can_have_side_effects() {
self.suggest_semicolon_at_end(expr.span, err);
}
@@ -1497,7 +1539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) {
- let unit = self.tcx.mk_unit();
+ let unit = Ty::new_unit(self.tcx);
let ty = self.check_block_with_expected(blk, ExpectHasType(unit));
// if the block produces a `!` value, that can always be
@@ -1610,7 +1652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
blk.span,
blk.hir_id,
expected_ty,
- self.tcx.mk_unit(),
+ Ty::new_unit(self.tcx),
);
}
if !self.consider_removing_semicolon(blk, expected_ty, err) {
@@ -1640,7 +1682,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
hir::Stmt {
kind:
hir::StmtKind::Expr(hir::Expr {
- kind: hir::ExprKind::Assign(..),
+ kind: hir::ExprKind::Assign(lhs, ..),
..
}),
..
@@ -1650,7 +1692,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} = blk
{
self.comes_from_while_condition(blk.hir_id, |_| {
- err.downgrade_to_delayed_bug();
+ // We cannot suppress the error if the LHS of assignment
+ // is a syntactic place expression because E0070 would
+ // not be emitted by `check_lhs_assignable`.
+ let res = self.typeck_results.borrow().expr_ty_opt(lhs);
+
+ if !lhs.is_syntactic_place_expr()
+ || res.references_error()
+ {
+ err.downgrade_to_delayed_bug();
+ }
})
}
}
@@ -1747,12 +1798,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
if let Err(guar) = ty.error_reported() {
// Override the types everywhere with `err()` to avoid knock on errors.
- let err = self.tcx.ty_error(guar);
+ let err = Ty::new_error(self.tcx, guar);
self.write_ty(hir_id, err);
self.write_ty(pat.hir_id, err);
- let local_ty = LocalTy { decl_ty: err, revealed_ty: err };
- self.locals.borrow_mut().insert(hir_id, local_ty);
- self.locals.borrow_mut().insert(pat.hir_id, local_ty);
+ self.locals.borrow_mut().insert(hir_id, err);
+ self.locals.borrow_mut().insert(pat.hir_id, err);
}
}
@@ -1776,8 +1826,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let result = self
.astconv()
.associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true);
- let ty =
- result.map(|(ty, _, _)| ty).unwrap_or_else(|guar| self.tcx().ty_error(guar));
+ let ty = result
+ .map(|(ty, _, _)| ty)
+ .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));
let ty = self.handle_raw_ty(path_span, ty);
let result = result.map(|(_, kind, def_id)| (kind, def_id));
@@ -1899,7 +1950,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// do that, so it's OK.
for (predicate, span) in instantiated
{
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
+ if let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
&& self.tcx.is_fn_trait(pred.def_id())
{
@@ -1931,7 +1982,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
traits::ObligationCause::dummy(),
self.param_env,
- ty::Binder::dummy(trait_ref),
+ trait_ref,
);
match SelectionContext::new(&self).select(&obligation) {
Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 67f45f9aa..20b34df99 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -163,7 +163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return fn_sig;
}
self.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
let normalized_fn_sig =
ocx.normalize(&ObligationCause::dummy(), self.param_env, fn_sig);
if ocx.select_all_or_error().is_empty() {
@@ -189,6 +189,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn errors_reported_since_creation(&self) -> bool {
self.tcx.sess.err_count() > self.err_count_on_creation
}
+
+ pub fn next_root_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
+ Ty::new_var(self.tcx, self.next_ty_var_id_in_universe(origin, ty::UniverseIndex::ROOT))
+ }
}
impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
@@ -222,9 +226,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
predicates: tcx.arena.alloc_from_iter(
self.param_env.caller_bounds().iter().filter_map(|predicate| {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data))
- if data.self_ty().is_param(index) =>
- {
+ ty::ClauseKind::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))
@@ -293,7 +295,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
trait_ref.substs,
);
- self.tcx().mk_projection(item_def_id, item_substs)
+ Ty::new_projection(self.tcx(), item_def_id, item_substs)
}
fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c4add4dbd..79a7c0161 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,6 +1,8 @@
use super::FnCtxt;
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
+use crate::errors::{
+ AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
+};
use crate::fluent_generated as fluent;
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
@@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
) -> bool {
let expr = expr.peel_blocks();
+ let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+
if let Some((suggestion, msg, applicability, verbose, annotation)) =
self.suggest_deref_or_ref(expr, found, expected)
{
@@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
return true;
- } else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
+ }
+
+ if self.suggest_else_fn_with_closure(err, expr, found, expected) {
return true;
- } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+ }
+
+ if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
&& let ty::FnDef(def_id, ..) = *found.kind()
&& let Some(sp) = self.tcx.hir().span_if_local(def_id)
{
@@ -343,97 +351,156 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(sp, format!("{descr} `{name}` defined here"));
}
return true;
- } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
+ }
+
+ if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
return true;
- } else {
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if !methods.is_empty() {
- let mut suggestions = methods.iter()
- .filter_map(|conversion_method| {
- let receiver_method_ident = expr.method_ident();
- if let Some(method_ident) = receiver_method_ident
- && method_ident.name == conversion_method.name
- {
- return None // do not suggest code that is already there (#53348)
- }
+ }
- let method_call_list = [sym::to_vec, sym::to_string];
- let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
- && receiver_method.ident.name == sym::clone
- && method_call_list.contains(&conversion_method.name)
- // If receiver is `.clone()` and found type has one of those methods,
- // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
- // so we remove the user's `clone` call.
- {
- vec![(
- receiver_method.ident.span,
- conversion_method.name.to_string()
- )]
- } else if expr.precedence().order()
- < ExprPrecedence::MethodCall.order()
- {
- vec![
- (expr.span.shrink_to_lo(), "(".to_string()),
- (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
- ]
- } else {
- vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
- };
- let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
- if let Some(name) = struct_pat_shorthand_field {
- sugg.insert(
- 0,
- (expr.span.shrink_to_lo(), format!("{}: ", name)),
- );
- }
- Some(sugg)
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.multipart_suggestions(
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- return true;
- }
- } else if let ty::Adt(found_adt, found_substs) = found.kind()
- && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
- && let ty::Adt(expected_adt, expected_substs) = expected.kind()
- && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
- && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
- && inner_ty.is_str()
- {
- let ty = found_substs.type_at(0);
- let mut peeled = ty;
- let mut ref_cnt = 0;
- while let ty::Ref(_, inner, _) = peeled.kind() {
- peeled = *inner;
- ref_cnt += 1;
- }
- if let ty::Adt(adt, _) = peeled.kind()
- && Some(adt.did()) == self.tcx.lang_items().string()
- {
- let sugg = if ref_cnt == 0 {
- ".as_deref()"
+ if !methods.is_empty() {
+ let mut suggestions = methods
+ .iter()
+ .filter_map(|conversion_method| {
+ let receiver_method_ident = expr.method_ident();
+ if let Some(method_ident) = receiver_method_ident
+ && method_ident.name == conversion_method.name
+ {
+ return None // do not suggest code that is already there (#53348)
+ }
+
+ let method_call_list = [sym::to_vec, sym::to_string];
+ let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
+ && receiver_method.ident.name == sym::clone
+ && method_call_list.contains(&conversion_method.name)
+ // If receiver is `.clone()` and found type has one of those methods,
+ // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
+ // to an owned type (`Vec` or `String`). These conversions clone internally,
+ // so we remove the user's `clone` call.
+ {
+ vec![(
+ receiver_method.ident.span,
+ conversion_method.name.to_string()
+ )]
+ } else if expr.precedence().order()
+ < ExprPrecedence::MethodCall.order()
+ {
+ vec![
+ (expr.span.shrink_to_lo(), "(".to_string()),
+ (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
+ ]
} else {
- ".map(|x| x.as_str())"
+ vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
};
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- fluent::hir_typeck_convert_to_str,
- sugg,
- Applicability::MachineApplicable,
- );
- return true;
- }
+ let struct_pat_shorthand_field =
+ self.maybe_get_struct_pattern_shorthand_field(expr);
+ if let Some(name) = struct_pat_shorthand_field {
+ sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
+ }
+ Some(sugg)
+ })
+ .peekable();
+ if suggestions.peek().is_some() {
+ err.multipart_suggestions(
+ "try using a conversion method",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ }
+
+ if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
+ self.deconstruct_option_or_result(found, expected)
+ && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
+ {
+ // Suggest removing any stray borrows (unless there's macro shenanigans involved).
+ let inner_expr = expr.peel_borrows();
+ if !inner_expr.span.eq_ctxt(expr.span) {
+ return false;
+ }
+ let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
+ None
+ } else {
+ Some(expr.span.shrink_to_lo().until(inner_expr.span))
+ };
+ // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
+ // `as_ref` and `as_deref` compatibility.
+ let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
+ self.can_eq(self.param_env, Ty::new_imm_ref(self.tcx,self.tcx.lifetimes.re_erased, found), expected)
+ });
+ // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
+ // but those checks need to be a bit more delicate and the benefit is diminishing.
+ if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
+ err.subdiagnostic(SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg: ".as_ref()",
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ } else if let Some((deref_ty, _)) =
+ self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
+ && self.can_eq(self.param_env, deref_ty, peeled)
+ && error_tys_equate_as_ref
+ {
+ err.subdiagnostic(SuggestConvertViaMethod {
+ span: expr.span.shrink_to_hi(),
+ sugg: ".as_deref()",
+ expected,
+ found,
+ borrow_removal_span,
+ });
+ return true;
+ } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
+ && Some(adt.did()) == self.tcx.lang_items().string()
+ && peeled.is_str()
+ // `Result::map`, conversely, does not take ref of the error type.
+ && error_tys.map_or(true, |(found, expected)| {
+ self.can_eq(self.param_env, found, expected)
+ })
+ {
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ fluent::hir_typeck_convert_to_str,
+ ".map(|x| x.as_str())",
+ Applicability::MachineApplicable,
+ );
+ return true;
}
}
false
}
+ fn deconstruct_option_or_result(
+ &self,
+ found_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
+ let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else {
+ return None;
+ };
+ let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else {
+ return None;
+ };
+ if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
+ && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
+ {
+ Some((found_substs.type_at(0), expected_substs.type_at(0), None))
+ } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
+ && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
+ {
+ Some((
+ found_substs.type_at(0),
+ expected_substs.type_at(0),
+ Some((found_substs.type_at(1), expected_substs.type_at(1))),
+ ))
+ } else {
+ None
+ }
+ }
+
/// 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(
@@ -448,7 +515,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.tcx.hir().is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
return false;
}
- if self.can_coerce(self.tcx.mk_box(found), expected) {
+ if self.can_coerce(Ty::new_box(self.tcx, found), expected) {
let suggest_boxing = match found.kind() {
ty::Tuple(tuple) if tuple.is_empty() => {
SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
@@ -528,9 +595,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
return false;
}
- let box_found = self.tcx.mk_box(found);
- let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap();
- let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap();
+ let box_found = Ty::new_box(self.tcx, found);
+ let pin_box_found = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin).unwrap();
+ let pin_found = Ty::new_lang_item(self.tcx, found, LangItem::Pin).unwrap();
match expected.kind() {
ty::Adt(def, _) if Some(def.did()) == pin_did => {
if self.can_coerce(pin_box_found, expected) {
@@ -874,7 +941,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let found = self.resolve_vars_with_obligations(found);
let in_loop = self.is_loop(id)
- || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id));
+ || self
+ .tcx
+ .hir()
+ .parent_iter(id)
+ .take_while(|(_, node)| {
+ // look at parents until we find the first body owner
+ node.body_id().is_none()
+ })
+ .any(|(parent_id, _)| self.is_loop(parent_id));
let in_local_statement = self.is_local_statement(id)
|| self
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 38445f284..4f45a24b2 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -1,4 +1,4 @@
-use crate::{FnCtxt, LocalTy};
+use crate::FnCtxt;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::PatKind;
@@ -48,7 +48,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
Self { fcx, outermost_fn_param_pat: None }
}
- fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> {
+ fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
match ty_opt {
None => {
// Infer the variable's type.
@@ -56,23 +56,20 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
kind: TypeVariableOriginKind::TypeInference,
span,
});
- self.fcx
- .locals
- .borrow_mut()
- .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty });
+ self.fcx.locals.borrow_mut().insert(nid, var_ty);
var_ty
}
Some(typ) => {
// Take type that the user specified.
self.fcx.locals.borrow_mut().insert(nid, typ);
- typ.revealed_ty
+ typ
}
}
}
- /// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have
- /// a type annotation, then the LocalTy stored will be the resolved type. This may be found
- /// again during type checking by querying [FnCtxt::local_ty] for the same hir_id.
+ /// Allocates a type for a declaration, which may have a type annotation. If it does have
+ /// a type annotation, then the [`Ty`] stored will be the resolved type. This may be found
+ /// again during type checking by querying [`FnCtxt::local_ty`] for the same hir_id.
fn declare(&mut self, decl: Declaration<'tcx>) {
let local_ty = match decl.ty {
Some(ref ty) => {
@@ -87,7 +84,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
.user_provided_types_mut()
.insert(ty.hir_id, c_ty);
- Some(LocalTy { decl_ty: o_ty.normalized, revealed_ty: o_ty.normalized })
+ Some(o_ty.normalized)
}
None => None,
};
@@ -96,7 +93,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
debug!(
"local variable {:?} is assigned type {}",
decl.pat,
- self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
+ self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&decl.hir_id).unwrap())
);
}
}
@@ -129,7 +126,17 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
self.fcx.require_type_is_sized(
var_ty,
p.span,
- traits::SizedArgumentType(Some(ty_span)),
+ // ty_span == ident.span iff this is a closure parameter with no type
+ // ascription, or if it's an implicit `self` parameter
+ traits::SizedArgumentType(
+ if ty_span == ident.span
+ && self.fcx.tcx.is_closure(self.fcx.body_id.into())
+ {
+ None
+ } else {
+ Some(ty_span)
+ },
+ ),
);
}
} else {
@@ -141,7 +148,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
debug!(
"pattern binding {} is assigned to {} with type {:?}",
ident,
- self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
+ self.fcx.ty_to_string(*self.fcx.locals.borrow().get(&p.hir_id).unwrap()),
var_ty
);
}
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
index e4a62ec05..b84c49186 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs
@@ -214,6 +214,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
+ | ExprKind::Become(..)
| ExprKind::InlineAsm(..)
| ExprKind::OffsetOf(..)
| ExprKind::Struct(..)
@@ -270,6 +271,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
| hir::Node::Variant(..)
| hir::Node::Field(..)
| hir::Node::AnonConst(..)
+ | hir::Node::ConstBlock(..)
| hir::Node::Stmt(..)
| hir::Node::PathSegment(..)
| hir::Node::Ty(..)
@@ -450,6 +452,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
}
}
+ ExprKind::Become(_call) => bug!("encountered a tail-call inside a generator"),
+
ExprKind::Call(f, args) => {
self.visit_expr(f);
for arg in args {
diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
index 019fb86f5..86ea092bc 100644
--- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
+++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs
@@ -122,7 +122,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
self.fcx
.need_type_info_err_in_generator(self.kind, span, unresolved_term)
- .span_note(yield_data.span, &*note)
+ .span_note(yield_data.span, note)
.emit();
}
} else {
@@ -269,7 +269,7 @@ pub fn resolve_interior<'a, 'tcx>(
},
_ => mk_bound_region(ty::BrAnon(None)),
};
- let r = fcx.tcx.mk_re_late_bound(current_depth, br);
+ let r = ty::Region::new_late_bound(fcx.tcx, current_depth, br);
r
});
captured_tys.insert(ty).then(|| {
@@ -295,7 +295,11 @@ pub fn resolve_interior<'a, 'tcx>(
let var = ty::BoundVar::from_usize(bound_vars.len());
bound_vars.push(ty::BoundVariableKind::Region(kind));
counter += 1;
- fcx.tcx.mk_re_late_bound(ty::INNERMOST, ty::BoundRegion { var, kind })
+ ty::Region::new_late_bound(
+ fcx.tcx,
+ ty::INNERMOST,
+ ty::BoundRegion { var, kind },
+ )
},
types: &mut |b| bug!("unexpected bound ty in binder: {b:?}"),
consts: &mut |b, ty| bug!("unexpected bound ct in binder: {b:?} {ty}"),
@@ -308,7 +312,8 @@ pub fn resolve_interior<'a, 'tcx>(
// Extract type components to build the witness type.
let type_list = fcx.tcx.mk_type_list_from_iter(type_causes.iter().map(|cause| cause.ty));
let bound_vars = fcx.tcx.mk_bound_variable_kinds(&bound_vars);
- let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars));
+ let witness =
+ Ty::new_generator_witness(fcx.tcx, ty::Binder::bind_with_vars(type_list, bound_vars));
drop(typeck_results);
// Store the generator types and spans into the typeck results for this generator.
@@ -357,7 +362,8 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
let ty =
self.interior_visitor.fcx.typeck_results.borrow().node_type(id);
let tcx = self.interior_visitor.fcx.tcx;
- let ty = tcx.mk_ref(
+ let ty = Ty::new_ref(
+ tcx,
// Use `ReErased` as `resolve_interior` is going to replace all the
// regions anyway.
tcx.lifetimes.re_erased,
@@ -573,7 +579,7 @@ fn check_must_not_suspend_ty<'tcx>(
let mut has_emitted = false;
for &(predicate, _) in fcx.tcx.explicit_item_bounds(def).skip_binder() {
// We only look at the `DefId`, so it is safe to skip the binder here.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
+ if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
predicate.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
@@ -686,7 +692,7 @@ fn check_must_not_suspend_def(
// Add optional reason note
if let Some(note) = attr.value_str() {
// FIXME(guswynn): consider formatting this better
- lint.span_note(data.source_span, note.as_str());
+ lint.span_note(data.source_span, note.to_string());
}
// Add some quick suggestions on what to do
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 294c3bb78..d5619af2a 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -30,7 +30,7 @@ pub struct Inherited<'tcx> {
pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>,
- pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
+ pub(super) locals: RefCell<HirIdMap<Ty<'tcx>>>,
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
@@ -80,14 +80,14 @@ impl<'tcx> Inherited<'tcx> {
let infcx = tcx
.infer_ctxt()
.ignoring_regions()
- .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
+ .with_opaque_type_inference(DefiningAnchor::Bind(def_id))
.build();
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
Inherited {
typeck_results,
+ fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(&infcx)),
infcx,
- fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)),
locals: RefCell::new(Default::default()),
deferred_sized_obligations: RefCell::new(Vec::new()),
deferred_call_resolutions: RefCell::new(Default::default()),
@@ -129,7 +129,7 @@ impl<'tcx> Inherited<'tcx> {
let infer_var_info = &mut self.infer_var_info.borrow_mut();
// (*) binder skipped
- if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(tpred)) = obligation.predicate.kind().skip_binder()
&& let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t))
&& self.tcx.lang_items().sized_trait().is_some_and(|st| st != tpred.trait_ref.def_id)
{
@@ -143,7 +143,7 @@ impl<'tcx> Inherited<'tcx> {
.kind()
.rebind(
// (*) binder moved here
- ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(self.tcx, new_self_ty)))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(tpred.with_self_ty(self.tcx, new_self_ty)))
),
);
// Don't report overflow errors. Otherwise equivalent to may_hold.
@@ -152,7 +152,7 @@ impl<'tcx> Inherited<'tcx> {
}
}
- if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) =
obligation.predicate.kind().skip_binder()
{
// If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 3c5eafd94..e58efc9d1 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -81,9 +81,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// Try to display a sensible error with as much information as possible.
- let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
- Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
+ let skeleton_string = |ty: Ty<'tcx>, sk: Result<_, &_>| match sk {
Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"),
+ Ok(SizeSkeleton::Known(size)) => {
+ if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
+ format!("{} bits", v)
+ } else {
+ // `u128` should definitely be able to hold the size of different architectures
+ // larger sizes should be reported as error `are too big for the current architecture`
+ // otherwise we have a bug somewhere
+ bug!("{:?} overflow for u128", size)
+ }
+ }
Ok(SizeSkeleton::Generic(size)) => {
if let Some(size) = size.try_eval_target_usize(tcx, self.param_env) {
format!("{size} bytes")
@@ -92,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
Err(LayoutError::Unknown(bad)) => {
- if bad == ty {
+ if *bad == ty {
"this type does not have a fixed size".to_owned()
} else {
format!("size can vary because of {bad}")
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index b97b55d8f..6f82ffcfe 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -5,7 +5,6 @@
#![feature(box_patterns)]
#![feature(min_specialization)]
#![feature(control_flow_enum)]
-#![feature(drain_filter)]
#![feature(option_as_slice)]
#![allow(rustc::potential_query_instability)]
#![recursion_limit = "256"]
@@ -90,13 +89,6 @@ macro_rules! type_error_struct {
})
}
-/// The type of a local binding, including the revealed type for anon types.
-#[derive(Copy, Clone, Debug)]
-pub struct LocalTy<'tcx> {
- decl_ty: Ty<'tcx>,
- revealed_ty: Ty<'tcx>,
-}
-
/// If this `DefId` is a "primary tables entry", returns
/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
///
@@ -162,7 +154,7 @@ fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tc
fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
let fallback = move || {
let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
- tcx.ty_error_with_message(span, "diagnostic only typeck table used")
+ Ty::new_error_with_message(tcx, span, "diagnostic only typeck table used")
};
typeck_with_fallback(tcx, def_id, fallback)
}
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index 78171e0b2..a1aa09084 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -277,9 +277,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
adjustment::Adjust::Deref(overloaded) => {
// Equivalent to *expr or something similar.
let base = if let Some(deref) = overloaded {
- let ref_ty = self
- .tcx()
- .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl });
+ let ref_ty = Ty::new_ref(
+ self.tcx(),
+ deref.region,
+ ty::TypeAndMut { ty: target, mutbl: deref.mutbl },
+ );
self.cat_rvalue(expr.hir_id, expr.span, ref_ty)
} else {
previous()?
@@ -361,6 +363,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
| hir::ExprKind::AssignOp(..)
| hir::ExprKind::Closure { .. }
| hir::ExprKind::Ret(..)
+ | hir::ExprKind::Become(..)
| hir::ExprKind::Unary(..)
| hir::ExprKind::Yield(..)
| hir::ExprKind::MethodCall(..)
@@ -488,7 +491,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
let ty::Ref(region, _, mutbl) = *base_ty.kind() else {
span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
};
- let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+ let ref_ty = Ty::new_ref(self.tcx(), region, ty::TypeAndMut { ty: place_ty, mutbl });
let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty);
self.cat_deref(expr, base)
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 98529b666..87edb8031 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -10,7 +10,7 @@ use rustc_hir_analysis::astconv::generics::{
use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall};
use rustc_infer::infer::{self, DefineOpaqueTypes, InferOk};
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::{self, SubstsRef};
@@ -26,6 +26,7 @@ struct ConfirmContext<'a, 'tcx> {
span: Span,
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
+ skip_record_for_diagnostics: bool,
}
impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> {
@@ -59,6 +60,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
confirm_cx.confirm(unadjusted_self_ty, pick, segment)
}
+
+ pub fn confirm_method_for_diagnostic(
+ &self,
+ span: Span,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ unadjusted_self_ty: Ty<'tcx>,
+ pick: &probe::Pick<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ ) -> ConfirmResult<'tcx> {
+ let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
+ confirm_cx.skip_record_for_diagnostics = true;
+ confirm_cx.confirm(unadjusted_self_ty, pick, segment)
+ }
}
impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
@@ -68,7 +83,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
call_expr: &'tcx hir::Expr<'tcx>,
) -> ConfirmContext<'a, 'tcx> {
- ConfirmContext { fcx, span, self_expr, call_expr }
+ ConfirmContext { fcx, span, self_expr, call_expr, skip_record_for_diagnostics: false }
}
fn confirm(
@@ -128,7 +143,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// a custom error in that case.
if illegal_sized_bound.is_none() {
self.add_obligations(
- self.tcx.mk_fn_ptr(method_sig),
+ Ty::new_fn_ptr(self.tcx, method_sig),
all_substs,
method_predicates,
pick.item.def_id,
@@ -156,7 +171,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// time writing the results into the various typeck results.
let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty);
let Some((ty, n)) = autoderef.nth(pick.autoderefs) else {
- return self.tcx.ty_error_with_message(
+ return Ty::new_error_with_message(self.tcx,
rustc_span::DUMMY_SP,
format!("failed autoderef {}", pick.autoderefs),
);
@@ -164,7 +179,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
assert_eq!(n, pick.autoderefs);
let mut adjustments = self.adjust_steps(&autoderef);
- let mut target = self.structurally_resolved_type(autoderef.span(), ty);
+ let mut target = self.structurally_resolve_type(autoderef.span(), ty);
match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
@@ -172,7 +187,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// Type we're wrapping in a reference, used later for unsizing
let base_ty = target;
- target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target });
+ target = Ty::new_ref(self.tcx, region, ty::TypeAndMut { mutbl, ty: target });
// Method call receivers are the primary use case
// for two-phase borrows.
@@ -185,31 +200,35 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
if unsize {
let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
- self.tcx.mk_slice(*elem_ty)
+ Ty::new_slice(self.tcx, *elem_ty)
} else {
bug!(
"AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
base_ty
)
};
- target = self
- .tcx
- .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty });
- adjustments
- .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target });
+ target = Ty::new_ref(
+ self.tcx,
+ region,
+ ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty },
+ );
+ adjustments.push(Adjustment {
+ kind: Adjust::Pointer(PointerCoercion::Unsize),
+ target,
+ });
}
}
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
target = match target.kind() {
&ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
assert!(mutbl.is_mut());
- self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
+ Ty::new_ptr(self.tcx, ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
}
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
};
adjustments.push(Adjustment {
- kind: Adjust::Pointer(PointerCast::MutToConstPointer),
+ kind: Adjust::Pointer(PointerCoercion::MutToConstPointer),
target,
});
}
@@ -219,7 +238,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
self.register_predicates(autoderef.into_obligations());
// Write out the final adjustments.
- self.apply_adjustments(self.self_expr, adjustments);
+ if !self.skip_record_for_diagnostics {
+ self.apply_adjustments(self.self_expr, adjustments);
+ }
target
}
@@ -453,7 +474,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
});
debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation);
- self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+
+ if !self.skip_record_for_diagnostics {
+ self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation);
+ }
}
self.normalize(self.span, substs)
@@ -586,9 +610,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
traits::elaborate(self.tcx, predicates.predicates.iter().copied())
// We don't care about regions here.
.filter_map(|pred| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
- if trait_pred.def_id() == sized_def_id =>
- {
+ ty::ClauseKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => {
let span = predicates
.iter()
.find_map(|(p, span)| if p == pred { Some(span) } else { None })
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index 6f4d674ba..e52cea188 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -205,9 +205,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let Some(span) = result.illegal_sized_bound {
let mut needs_mut = false;
if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
- let trait_type = self
- .tcx
- .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
+ let trait_type = Ty::new_ref(
+ self.tcx,
+ *region,
+ ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
+ );
// We probe again to see if there might be a borrow mutability discrepancy.
match self.lookup_probe(
segment.ident,
@@ -254,6 +256,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Ok(result.callee)
}
+ pub fn lookup_method_for_diagnostic(
+ &self,
+ self_ty: Ty<'tcx>,
+ segment: &hir::PathSegment<'_>,
+ span: Span,
+ call_expr: &'tcx hir::Expr<'tcx>,
+ self_expr: &'tcx hir::Expr<'tcx>,
+ ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
+ let pick = self.lookup_probe_for_diagnostic(
+ segment.ident,
+ self_ty,
+ call_expr,
+ ProbeScope::TraitsInScope,
+ None,
+ )?;
+
+ Ok(self
+ .confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
+ .callee)
+ }
+
#[instrument(level = "debug", skip(self, call_expr))]
pub fn lookup_probe(
&self,
@@ -443,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
));
// Also add an obligation for the method type being well-formed.
- let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig));
+ let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
debug!(
"lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}",
method_ty, obligation
@@ -452,7 +475,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
tcx,
obligation.cause,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ method_ty.into(),
+ ))),
));
let callee = MethodCallee { def_id, substs, sig: fn_sig };
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 9f3d35a77..03a3eebbd 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -9,10 +9,10 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use rustc_hir_analysis::astconv::InferCtxtExt as _;
use rustc_hir_analysis::autoderef::{self, Autoderef};
use rustc_infer::infer::canonical::OriginalQueryValues;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
+use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::{self, InferOk, TyCtxtInferExt};
use rustc_middle::middle::stability;
@@ -449,15 +449,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
} else {
- // Encountered a real ambiguity, so abort the lookup. If `ty` is not
- // an `Err`, report the right "type annotations needed" error pointing
- // to it.
+ // Ended up encountering a type variable when doing autoderef,
+ // but it may not be a type variable after processing obligations
+ // in our local `FnCtxt`, so don't call `structurally_resolve_type`.
let ty = &bad_ty.ty;
let ty = self
.probe_instantiate_query_response(span, &orig_values, ty)
.unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty));
- let ty = self.structurally_resolved_type(span, ty.value);
- assert!(matches!(ty.kind(), ty::Error(_)));
+ let ty = self.resolve_vars_if_possible(ty.value);
+ let guar = match *ty.kind() {
+ ty::Infer(ty::TyVar(_)) => self
+ .err_ctxt()
+ .emit_inference_failure_err(self.body_id, span, ty.into(), E0282, true)
+ .emit(),
+ ty::Error(guar) => guar,
+ _ => bug!("unexpected bad final type in method autoderef"),
+ };
+ self.demand_eqtype(span, ty, Ty::new_error(self.tcx, guar));
return Err(MethodError::NoMatch(NoMatchData {
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
@@ -543,7 +551,7 @@ fn method_autoderef_steps<'tcx>(
steps.push(CandidateStep {
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
- infcx.tcx.mk_slice(*elem_ty),
+ Ty::new_slice(infcx.tcx, *elem_ty),
),
autoderefs: dereferences,
// this could be from an unsafe deref if we had
@@ -826,7 +834,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| {
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::ClauseKind::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))
@@ -834,20 +842,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
_ => None,
}
}
- ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::TypeOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => None,
}
});
@@ -954,7 +954,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
trait_def_id: DefId,
) {
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id);
- let trait_substs = self.fresh_item_substs(trait_def_id);
+ let trait_substs = self.fresh_substs_for_item(self.span, trait_def_id);
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_substs);
if self.tcx.is_trait_alias(trait_def_id) {
@@ -1215,7 +1215,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
// In general, during probing we erase regions.
let region = tcx.lifetimes.re_erased;
- let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl });
+ let autoref_ty = Ty::new_ref(tcx, region, ty::TypeAndMut { ty: self_ty, mutbl });
self.pick_method(autoref_ty, unstable_candidates).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
@@ -1245,7 +1245,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
};
let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not };
- let const_ptr_ty = self.tcx.mk_ptr(const_self_ty);
+ let const_ptr_ty = Ty::new_ptr(self.tcx, const_self_ty);
self.pick_method(const_ptr_ty, unstable_candidates).map(|r| {
r.map(|mut pick| {
pick.autoderefs = step.autoderefs;
@@ -1441,8 +1441,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
trait_ref: ty::TraitRef<'tcx>,
) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> {
let cause = traits::ObligationCause::misc(self.span, self.body_id);
- let predicate = ty::Binder::dummy(trait_ref);
- let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, predicate);
+ let obligation = traits::Obligation::new(self.tcx, cause, self.param_env, trait_ref);
traits::SelectionContext::new(self).select(&obligation)
}
@@ -1899,7 +1898,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
&self,
impl_def_id: DefId,
) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) {
- (self.tcx.type_of(impl_def_id), self.fresh_item_substs(impl_def_id))
+ (self.tcx.type_of(impl_def_id), self.fresh_substs_for_item(self.span, impl_def_id))
}
/// Replaces late-bound-regions bound by `value` with `'static` using
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index e04cc44b5..5f924f309 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -211,7 +211,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
if needs_mut {
- let trait_type = self.tcx.mk_ref(
+ let trait_type = Ty::new_ref(
+ self.tcx,
*region,
ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
);
@@ -473,6 +474,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut custom_span_label = false;
let static_candidates = &mut no_match_data.static_candidates;
+
+ // `static_candidates` may have same candidates appended by
+ // inherent and extension, which may result in incorrect
+ // diagnostic.
+ static_candidates.dedup();
+
if !static_candidates.is_empty() {
err.note(
"found the following associated functions; to be used as methods, \
@@ -536,7 +543,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut unimplemented_traits = FxHashMap::default();
let mut unimplemented_traits_only = true;
for (predicate, _parent_pred, cause) in unsatisfied_predicates {
- if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) =
+ if let (ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)), Some(cause)) =
(predicate.kind().skip_binder(), cause.as_ref())
{
if p.trait_ref.self_ty() != rcvr_ty {
@@ -563,7 +570,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// because of some non-Clone item being iterated over.
for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(p))
if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {}
_ => {
unimplemented_traits_only = false;
@@ -575,7 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut collect_type_param_suggestions =
|self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
// We don't care about regions here, so it's fine to skip the binder here.
- if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) =
+ if let (ty::Param(_), ty::PredicateKind::Clause(ty::ClauseKind::Trait(p))) =
(self_ty.kind(), parent_pred.kind().skip_binder())
{
let hir = self.tcx.hir();
@@ -635,13 +642,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut format_pred = |pred: ty::Predicate<'tcx>| {
let bound_predicate = pred.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(pred)) => {
let pred = bound_predicate.rebind(pred);
// `<Foo as Iterator>::Item = String`.
let projection_ty = pred.skip_binder().projection_ty;
let substs_with_infer_self = tcx.mk_substs_from_iter(
- iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into())
+ iter::once(Ty::new_var(tcx, ty::TyVid::from_u32(0)).into())
.chain(projection_ty.substs.iter().skip(1)),
);
@@ -659,7 +666,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
bound_span_label(projection_ty.self_ty(), &obligation, &quiet);
Some((obligation, projection_ty.self_ty()))
}
- ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(poly_trait_ref)) => {
let p = poly_trait_ref.trait_ref;
let self_ty = p.self_ty();
let path = p.print_only_trait_path();
@@ -690,7 +697,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
// Don't point out the span of `WellFormed` predicates.
- if !matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) {
+ if !matches!(
+ p.kind().skip_binder(),
+ ty::PredicateKind::Clause(
+ ty::ClauseKind::Projection(..) | ty::ClauseKind::Trait(..)
+ )
+ ) {
continue;
};
@@ -731,7 +743,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let sized_pred =
unsatisfied_predicates.iter().any(|(pred, _, _)| {
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
&& pred.polarity == ty::ImplPolarity::Positive
}
@@ -2003,16 +2015,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) {
let all_local_types_needing_impls =
errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => match pred.self_ty().kind() {
- ty::Adt(def, _) => def.did().is_local(),
- _ => false,
- },
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+ match pred.self_ty().kind() {
+ ty::Adt(def, _) => def.did().is_local(),
+ _ => false,
+ }
+ }
_ => false,
});
let mut preds: Vec<_> = errors
.iter()
.filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => Some(pred),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred),
_ => None,
})
.collect();
@@ -2083,7 +2097,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut derives = Vec::<(String, Span, Symbol)>::new();
let mut traits = Vec::new();
for (pred, _, _) in unsatisfied_predicates {
- let Some(ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))) =
+ let Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) =
pred.kind().no_bound_vars()
else {
continue
@@ -2395,8 +2409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// just this list.
for (rcvr_ty, post) in &[
(rcvr_ty, ""),
- (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
- (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
+ (Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
+ (Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
] {
match self.lookup_probe_for_diagnostic(
item_name,
@@ -2431,10 +2445,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
for (rcvr_ty, pre) in &[
- (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"),
- (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"),
- (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
- (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
+ (Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::OwnedBox), "Box::new"),
+ (Ty::new_lang_item(self.tcx, *rcvr_ty, LangItem::Pin), "Pin::new"),
+ (Ty::new_diagnostic_item(self.tcx, *rcvr_ty, sym::Arc), "Arc::new"),
+ (Ty::new_diagnostic_item(self.tcx, *rcvr_ty, sym::Rc), "Rc::new"),
] {
if let Some(new_rcvr_t) = *rcvr_ty
&& let Ok(pick) = self.lookup_probe_for_diagnostic(
@@ -2518,10 +2532,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
match p.kind().skip_binder() {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.
- ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
t.def_id() == info.def_id
}
- ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
p.projection_ty.def_id == info.def_id
}
_ => false,
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index b8bf2b691..1eae258c1 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -38,7 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let ty =
if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
- self.tcx.mk_unit()
+ Ty::new_unit(self.tcx)
} else {
return_ty
};
@@ -297,7 +297,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
// error types are considered "builtin"
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => {
- self.tcx.ty_error_misc()
+ Ty::new_misc_error(self.tcx)
}
Err(errors) => {
let (_, trait_def_id) =
@@ -521,8 +521,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
+
+ // Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
+ // pointer + {integer} or pointer - pointer.
+ if op.span.can_be_used_for_suggestions() {
+ match op.node {
+ hir::BinOpKind::Add if lhs_ty.is_unsafe_ptr() && rhs_ty.is_integral() => {
+ err.multipart_suggestion(
+ "consider using `wrapping_add` or `add` for pointer + {integer}",
+ vec![
+ (
+ lhs_expr.span.between(rhs_expr.span),
+ ".wrapping_add(".to_owned(),
+ ),
+ (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ hir::BinOpKind::Sub => {
+ if lhs_ty.is_unsafe_ptr() && rhs_ty.is_integral() {
+ err.multipart_suggestion(
+ "consider using `wrapping_sub` or `sub` for pointer - {integer}",
+ vec![
+ (lhs_expr.span.between(rhs_expr.span), ".wrapping_sub(".to_owned()),
+ (rhs_expr.span.shrink_to_hi(), ")".to_owned()),
+ ],
+ Applicability::MaybeIncorrect
+ );
+ }
+
+ if lhs_ty.is_unsafe_ptr() && rhs_ty.is_unsafe_ptr() {
+ err.multipart_suggestion(
+ "consider using `offset_from` for pointer - pointer if the pointers point to the same allocation",
+ vec![
+ (lhs_expr.span.shrink_to_lo(), "unsafe { ".to_owned()),
+ (lhs_expr.span.between(rhs_expr.span), ".offset_from(".to_owned()),
+ (rhs_expr.span.shrink_to_hi(), ") }".to_owned()),
+ ],
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ _ => {}
+ }
+ }
+
let reported = err.emit();
- self.tcx.ty_error(reported)
+ Ty::new_error(self.tcx, reported)
}
};
@@ -706,7 +752,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
err.emit()
});
- self.tcx.ty_error(guar)
+ Ty::new_error(self.tcx, guar)
}
}
}
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 5af955d31..42f4531c0 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -393,9 +393,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// They can denote both statically and dynamically-sized byte arrays.
let mut pat_ty = ty;
if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(..), .. }) = lt.kind {
- let expected = self.structurally_resolved_type(span, expected);
- if let ty::Ref(_, inner_ty, _) = expected.kind()
- && matches!(inner_ty.kind(), ty::Slice(_))
+ let expected = self.structurally_resolve_type(span, expected);
+ if let ty::Ref(_, inner_ty, _) = *expected.kind()
+ && self.try_structurally_resolve_type(span, inner_ty).is_slice()
{
let tcx = self.tcx;
trace!(?lt.hir_id.local_id, "polymorphic byte string lit");
@@ -403,7 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.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));
+ pat_ty = Ty::new_imm_ref(tcx,tcx.lifetimes.re_static, Ty::new_slice(tcx,tcx.types.u8));
}
}
@@ -412,7 +412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let expected = self.resolve_vars_if_possible(expected);
pat_ty = match expected.kind() {
ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
- ty::Str => tcx.mk_static_str(),
+ ty::Str => Ty::new_static_str(tcx,),
_ => pat_ty,
};
}
@@ -474,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// There exists a side that didn't meet our criteria that the end-point
// be of a numeric or char type, as checked in `calc_side` above.
let guar = self.emit_err_pat_range(span, lhs, rhs);
- return self.tcx.ty_error(guar);
+ return Ty::new_error(self.tcx, guar);
}
// Unify each side with `expected`.
@@ -494,14 +494,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
demand_eqtype(&mut rhs, lhs);
if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
- return self.tcx.ty_error_misc();
+ return Ty::new_misc_error(self.tcx);
}
// Find the unified type and check if it's of numeric or char type again.
// This check is needed if both sides are inference variables.
// We require types to be resolved here so that we emit inference failure
// rather than "_ is not a char or numeric".
- let ty = self.structurally_resolved_type(span, expected);
+ let ty = self.structurally_resolve_type(span, expected);
if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
if let Some((ref mut fail, _, _)) = lhs {
*fail = true;
@@ -510,7 +510,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
*fail = true;
}
let guar = self.emit_err_pat_range(span, lhs, rhs);
- return self.tcx.ty_error(guar);
+ return Ty::new_error(self.tcx, guar);
}
ty
}
@@ -594,7 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm);
- let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
+ let local_ty = self.local_ty(pat.span, pat.hir_id);
let eq_ty = match bm {
ty::BindByReference(mutbl) => {
// If the binding is like `ref x | ref mut x`,
@@ -635,7 +635,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty: Ty<'tcx>,
ti: TopInfo<'tcx>,
) {
- let var_ty = self.local_ty(span, var_id).decl_ty;
+ let var_ty = self.local_ty(span, var_id);
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
let hir = self.tcx.hir();
let var_ty = self.resolve_vars_with_obligations(var_ty);
@@ -848,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) {
Ok(data) => data,
Err(guar) => {
- let err = self.tcx.ty_error(guar);
+ let err = Ty::new_error(self.tcx, guar);
for field in fields {
let ti = ti;
self.check_pat(field.pat, err, def_bm, ti);
@@ -864,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) {
pat_ty
} else {
- self.tcx.ty_error_misc()
+ Ty::new_misc_error(self.tcx)
}
}
@@ -884,12 +884,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Res::Err => {
let e = tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => {
let expected = "unit struct, unit variant or constant";
let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, "E0533", expected);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
Res::SelfCtor(..)
| Res::Def(
@@ -1032,7 +1032,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let tcx = self.tcx;
let on_error = |e| {
for pat in subpats {
- self.check_pat(pat, tcx.ty_error(e), def_bm, ti);
+ self.check_pat(pat, Ty::new_error(tcx, e), def_bm, ti);
}
};
let report_unexpected_res = |res: Res| {
@@ -1049,7 +1049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
on_error(e);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
// Type-check the path.
@@ -1057,7 +1057,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id);
if !pat_ty.is_fn() {
let e = report_unexpected_res(res);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
let variant = match res {
@@ -1065,11 +1065,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted");
self.set_tainted_by_errors(e);
on_error(e);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => {
let e = report_unexpected_res(res);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res),
_ => bug!("unexpected pattern resolution: {:?}", res),
@@ -1112,7 +1112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let e =
self.e0023(pat.span, res, qpath, subpats, &variant.fields.raw, expected, had_err);
on_error(e);
- return tcx.ty_error(e);
+ return Ty::new_error(tcx, e);
}
pat_ty
}
@@ -1289,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut expected_len = elements.len();
if ddpos.as_opt_usize().is_some() {
// Require known type only when `..` is present.
- if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() {
+ if let ty::Tuple(tys) = self.structurally_resolve_type(span, expected).kind() {
expected_len = tys.len();
}
}
@@ -1303,16 +1303,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
});
let element_tys = tcx.mk_type_list_from_iter(element_tys_iter);
- let pat_ty = tcx.mk_tup(element_tys);
+ let pat_ty = Ty::new_tup(tcx, element_tys);
if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) {
let reported = err.emit();
// Walk subpatterns with an expected type of `err` in this case to silence
// further errors being emitted when using the bindings. #50333
- let element_tys_iter = (0..max_len).map(|_| tcx.ty_error(reported));
+ let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported));
for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
- self.check_pat(elem, tcx.ty_error(reported), def_bm, ti);
+ self.check_pat(elem, Ty::new_error(tcx, reported), def_bm, ti);
}
- tcx.mk_tup_from_iter(element_tys_iter)
+ Ty::new_tup_from_iter(tcx, element_tys_iter)
} else {
for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
self.check_pat(elem, element_tys[i], def_bm, ti);
@@ -1357,7 +1357,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Occupied(occupied) => {
no_field_errors = false;
let guar = self.error_field_already_bound(span, field.ident, *occupied.get());
- tcx.ty_error(guar)
+ Ty::new_error(tcx, guar)
}
Vacant(vacant) => {
vacant.insert(span);
@@ -1371,7 +1371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.unwrap_or_else(|| {
inexistent_fields.push(field);
no_field_errors = false;
- tcx.ty_error_misc()
+ Ty::new_misc_error(tcx)
})
}
};
@@ -1951,12 +1951,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
kind: TypeVariableOriginKind::TypeInference,
span: inner.span,
});
- let box_ty = tcx.mk_box(inner_ty);
+ let box_ty = Ty::new_box(tcx, inner_ty);
self.demand_eqtype_pat(span, expected, box_ty, ti);
(box_ty, inner_ty)
}
Err(guar) => {
- let err = tcx.ty_error(guar);
+ let err = Ty::new_error(tcx, guar);
(err, err)
}
};
@@ -2007,7 +2007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
Err(guar) => {
- let err = tcx.ty_error(guar);
+ let err = Ty::new_error(tcx, guar);
(err, err)
}
};
@@ -2019,7 +2019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> {
let region = self.next_region_var(infer::PatternRegion(span));
let mt = ty::TypeAndMut { ty, mutbl };
- self.tcx.mk_ref(region, mt)
+ Ty::new_ref(self.tcx, region, mt)
}
/// Type check a slice pattern.
@@ -2042,7 +2042,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
def_bm: BindingMode,
ti: TopInfo<'tcx>,
) -> Ty<'tcx> {
- let expected = self.structurally_resolved_type(span, expected);
+ let expected = self.structurally_resolve_type(span, expected);
let (element_ty, opt_slice_ty, inferred) = match *expected.kind() {
// An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
ty::Array(element_ty, len) => {
@@ -2061,7 +2061,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.error_reported()
.err()
.unwrap_or_else(|| self.error_expected_array_or_slice(span, expected, ti));
- let err = self.tcx.ty_error(guar);
+ let err = Ty::new_error(self.tcx, guar);
(err, Some(err), err)
}
};
@@ -2108,7 +2108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if let Some(pat_len) = len.checked_sub(min_len) {
// The variable-length pattern was there,
// so it has an array type with the remaining elements left as its size...
- return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty);
+ return (Some(Ty::new_array(self.tcx, element_ty, pat_len)), arr_ty);
} else {
// ...however, in this case, there were no remaining elements.
// That is, the slice pattern requires more than the array type offers.
@@ -2117,7 +2117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else if slice.is_none() {
// We have a pattern with a fixed length,
// which we can use to infer the length of the array.
- let updated_arr_ty = self.tcx.mk_array(element_ty, min_len);
+ let updated_arr_ty = Ty::new_array(self.tcx, element_ty, min_len);
self.demand_eqtype(span, updated_arr_ty, arr_ty);
return (None, updated_arr_ty);
} else {
@@ -2128,7 +2128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
// If we get here, we must have emitted an error.
- (Some(self.tcx.ty_error(guar)), arr_ty)
+ (Some(Ty::new_error(self.tcx, guar)), arr_ty)
}
fn error_scrutinee_inconsistent_length(
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index e2b1dc007..fd43b475e 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -6,7 +6,7 @@ use rustc_hir as hir;
use rustc_hir_analysis::autoderef::Autoderef;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCoercion};
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Ident};
@@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
let reported = err.emit();
- Some((self.tcx.ty_error(reported), self.tcx.ty_error(reported)))
+ Some((Ty::new_error(self.tcx, reported), Ty::new_error(self.tcx, reported)))
}
/// To type-check `base_expr[index_expr]`, we progressively autoderef
@@ -107,7 +107,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
index_expr: &hir::Expr<'_>,
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
let adjusted_ty =
- self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false));
+ self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
debug!(
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
index_ty={:?})",
@@ -138,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if unsize {
// We only unsize arrays here.
if let ty::Array(element_ty, _) = adjusted_ty.kind() {
- self_ty = self.tcx.mk_slice(*element_ty);
+ self_ty = Ty::new_slice(self.tcx, *element_ty);
} else {
continue;
}
@@ -162,7 +162,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
adjustments.push(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)),
- target: self.tcx.mk_ref(
+ target: Ty::new_ref(
+ self.tcx,
*region,
ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
),
@@ -172,7 +173,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
if unsize {
adjustments.push(Adjustment {
- kind: Adjust::Pointer(PointerCast::Unsize),
+ kind: Adjust::Pointer(PointerCoercion::Unsize),
target: method.sig.inputs()[0],
});
}
@@ -427,9 +428,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
allow_two_phase_borrow: AllowTwoPhase::No,
};
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl));
- adjustment.target = self
- .tcx
- .mk_ref(*region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
+ adjustment.target = Ty::new_ref(
+ self.tcx,
+ *region,
+ ty::TypeAndMut { ty: source, mutbl: mutbl.into() },
+ );
}
source = adjustment.target;
}
@@ -438,7 +441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let [
..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
- Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target },
+ Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), ref mut target },
] = adjustments[..]
{
*target = method.sig.inputs()[0];
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 9458099f5..208c40a39 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -300,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Build a tuple (U0..Un) of the final upvar types U0..Un
// and unify the upvar tuple type in the closure with it:
- let final_tupled_upvars_type = self.tcx.mk_tup(&final_upvar_tys);
+ let final_tupled_upvars_type = Ty::new_tup(self.tcx, &final_upvar_tys);
self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type);
let fake_reads = delegate
@@ -314,8 +314,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.typeck_results.borrow_mut().closure_size_eval.insert(
closure_def_id,
ClosureSizeProfileData {
- before_feature_tys: self.tcx.mk_tup(&before_feature_tys),
- after_feature_tys: self.tcx.mk_tup(&after_feature_tys),
+ before_feature_tys: Ty::new_tup(self.tcx, &before_feature_tys),
+ after_feature_tys: Ty::new_tup(self.tcx, &after_feature_tys),
},
);
}
@@ -1665,9 +1665,11 @@ fn apply_capture_kind_on_capture_ty<'tcx>(
) -> Ty<'tcx> {
match capture_kind {
ty::UpvarCapture::ByValue => ty,
- ty::UpvarCapture::ByRef(kind) => {
- tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() })
- }
+ ty::UpvarCapture::ByRef(kind) => Ty::new_ref(
+ tcx,
+ region.unwrap(),
+ ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() },
+ ),
}
}
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 0f21fc1e6..106457536 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -11,10 +11,9 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
use rustc_middle::hir::place::Place as HirPlace;
use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion};
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::TypeckResults;
use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;
@@ -137,7 +136,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) {
debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty);
- assert!(!ty.has_infer() && !ty.has_placeholders() && !ty.has_free_regions());
+ assert!(
+ !ty.has_infer() && !ty.has_placeholders() && !ty.has_free_regions(),
+ "{ty} can't be put into typeck results"
+ );
self.typeck_results.node_types_mut().insert(hir_id, ty);
}
@@ -148,31 +150,25 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr<'_>) {
match e.kind {
hir::ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, inner) => {
- let inner_ty = self.fcx.node_ty(inner.hir_id);
- let inner_ty = self.fcx.resolve_vars_if_possible(inner_ty);
+ let inner_ty = self.typeck_results.node_type(inner.hir_id);
if inner_ty.is_scalar() {
- let mut typeck_results = self.fcx.typeck_results.borrow_mut();
- typeck_results.type_dependent_defs_mut().remove(e.hir_id);
- typeck_results.node_substs_mut().remove(e.hir_id);
+ self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
+ self.typeck_results.node_substs_mut().remove(e.hir_id);
}
}
hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => {
- let lhs_ty = self.fcx.node_ty(lhs.hir_id);
- let lhs_ty = self.fcx.resolve_vars_if_possible(lhs_ty);
-
- let rhs_ty = self.fcx.node_ty(rhs.hir_id);
- let rhs_ty = self.fcx.resolve_vars_if_possible(rhs_ty);
+ let lhs_ty = self.typeck_results.node_type(lhs.hir_id);
+ let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
if lhs_ty.is_scalar() && rhs_ty.is_scalar() {
- let mut typeck_results = self.fcx.typeck_results.borrow_mut();
- typeck_results.type_dependent_defs_mut().remove(e.hir_id);
- typeck_results.node_substs_mut().remove(e.hir_id);
+ self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
+ self.typeck_results.node_substs_mut().remove(e.hir_id);
match e.kind {
hir::ExprKind::Binary(..) => {
if !op.node.is_by_value() {
- let mut adjustments = typeck_results.adjustments_mut();
+ let mut adjustments = self.typeck_results.adjustments_mut();
if let Some(a) = adjustments.get_mut(lhs.hir_id) {
a.pop();
}
@@ -182,7 +178,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
}
}
hir::ExprKind::AssignOp(..)
- if let Some(a) = typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
+ if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
{
a.pop();
}
@@ -200,16 +196,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
// if they are not we don't modify the expr, hence we bypass the ICE
fn is_builtin_index(
&mut self,
- typeck_results: &TypeckResults<'tcx>,
e: &hir::Expr<'_>,
base_ty: Ty<'tcx>,
index_ty: Ty<'tcx>,
) -> bool {
- if let Some(elem_ty) = base_ty.builtin_index() {
- let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;};
- let resolved_exp_ty = self.resolve(exp_ty, &e.span);
-
- elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize
+ if let Some(elem_ty) = base_ty.builtin_index()
+ && let Some(exp_ty) = self.typeck_results.expr_ty_opt(e)
+ {
+ elem_ty == exp_ty && index_ty == self.fcx.tcx.types.usize
} else {
false
}
@@ -221,38 +215,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
// usize-ish
fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) {
if let hir::ExprKind::Index(ref base, ref index) = e.kind {
- let mut typeck_results = self.fcx.typeck_results.borrow_mut();
-
// All valid indexing looks like this; might encounter non-valid indexes at this point.
- let base_ty = typeck_results
- .expr_ty_adjusted_opt(base)
- .map(|t| self.fcx.resolve_vars_if_possible(t).kind());
+ let base_ty = self.typeck_results.expr_ty_adjusted_opt(base);
if base_ty.is_none() {
// When encountering `return [0][0]` outside of a `fn` body we can encounter a base
// that isn't in the type table. We assume more relevant errors have already been
// emitted, so we delay an ICE if none have. (#64638)
self.tcx().sess.delay_span_bug(e.span, format!("bad base: `{:?}`", base));
}
- if let Some(ty::Ref(_, base_ty, _)) = base_ty {
- let index_ty = typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| {
- // When encountering `return [0][0]` outside of a `fn` body we would attempt
- // to access an nonexistent index. We assume that more relevant errors will
- // already have been emitted, so we only gate on this with an ICE if no
- // error has been emitted. (#64638)
- self.fcx.tcx.ty_error_with_message(
- e.span,
- format!("bad index {:?} for base: `{:?}`", index, base),
- )
- });
- let index_ty = self.fcx.resolve_vars_if_possible(index_ty);
- let resolved_base_ty = self.resolve(*base_ty, &base.span);
-
- if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) {
+ if let Some(base_ty) = base_ty
+ && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind()
+ {
+ let index_ty =
+ self.typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| {
+ // When encountering `return [0][0]` outside of a `fn` body we would attempt
+ // to access an nonexistent index. We assume that more relevant errors will
+ // already have been emitted, so we only gate on this with an ICE if no
+ // error has been emitted. (#64638)
+ Ty::new_error_with_message(
+ self.fcx.tcx,
+ e.span,
+ format!("bad index {:?} for base: `{:?}`", index, base),
+ )
+ });
+ if self.is_builtin_index(e, base_ty_inner, index_ty) {
// Remove the method call record
- typeck_results.type_dependent_defs_mut().remove(e.hir_id);
- typeck_results.node_substs_mut().remove(e.hir_id);
+ self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
+ self.typeck_results.node_substs_mut().remove(e.hir_id);
- if let Some(a) = typeck_results.adjustments_mut().get_mut(base.hir_id) {
+ if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) {
// Discard the need for a mutable borrow
// Extra adjustment made when indexing causes a drop
@@ -260,7 +251,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
// Since this is "after" the other adjustment to be
// discarded, we do an extra `pop()`
if let Some(Adjustment {
- kind: Adjust::Pointer(PointerCast::Unsize), ..
+ kind: Adjust::Pointer(PointerCoercion::Unsize), ..
}) = a.pop()
{
// So the borrow discard actually happens here
@@ -283,9 +274,6 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
- self.fix_scalar_builtin_expr(e);
- self.fix_index_builtin_expr(e);
-
match e.kind {
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let body = self.fcx.tcx.hir().body(body);
@@ -314,6 +302,9 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
self.visit_node_id(e.span, e.hir_id);
intravisit::walk_expr(self, e);
+
+ self.fix_scalar_builtin_expr(e);
+ self.fix_index_builtin_expr(e);
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
@@ -358,7 +349,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
intravisit::walk_local(self, l);
- let var_ty = self.fcx.local_ty(l.span, l.hir_id).decl_ty;
+ let var_ty = self.fcx.local_ty(l.span, l.hir_id);
let var_ty = self.resolve(var_ty, &l.span);
self.write_ty_to_typeck_results(l.hir_id, var_ty);
}
@@ -583,19 +574,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
continue;
}
- let hidden_type =
- self.tcx().erase_regions(hidden_type.remap_generic_params_to_declaration_params(
- opaque_type_key,
- self.tcx(),
- true,
- ));
-
+ // Here we only detect impl trait definition conflicts when they
+ // are equal modulo regions.
if let Some(last_opaque_ty) = self
.typeck_results
.concrete_opaque_types
- .insert(opaque_type_key.def_id, hidden_type)
+ .insert(opaque_type_key, hidden_type)
&& last_opaque_ty.ty != hidden_type.ty
{
+ assert!(!self.fcx.next_trait_solver());
hidden_type
.report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx())
.stash(
@@ -816,11 +803,15 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match self.fcx.fully_resolve(t) {
- Ok(t) if self.fcx.tcx.trait_solver_next() => {
+ Ok(t) if self.fcx.next_trait_solver() => {
// We must normalize erasing regions here, since later lints
// expect that types that show up in the typeck are fully
// normalized.
- self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t).unwrap_or(t)
+ if let Ok(t) = self.fcx.tcx.try_normalize_erasing_regions(self.fcx.param_env, t) {
+ t
+ } else {
+ EraseEarlyRegions { tcx: self.fcx.tcx }.fold_ty(t)
+ }
}
Ok(t) => {
// Do not anonymize late-bound regions
@@ -833,7 +824,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t);
let e = self.report_error(t);
self.replaced_with_error = Some(e);
- self.fcx.tcx.ty_error(e)
+ Ty::new_error(self.fcx.tcx, e)
}
}
}
@@ -850,7 +841,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
let e = self.report_error(ct);
self.replaced_with_error = Some(e);
- self.fcx.tcx.const_error(ct.ty(), e)
+ ty::Const::new_error(self.fcx.tcx, e, ct.ty())
}
}
}
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index 22bd12f2e..52a84b204 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -35,7 +35,7 @@
use crate::errors;
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING};
use rustc_graphviz as dot;
use rustc_hir as hir;
@@ -258,7 +258,7 @@ fn dump_graph(query: &DepGraphQuery) {
}
#[allow(missing_docs)]
-pub struct GraphvizDepGraph(FxHashSet<DepKind>, Vec<(DepKind, DepKind)>);
+pub struct GraphvizDepGraph(FxIndexSet<DepKind>, Vec<(DepKind, DepKind)>);
impl<'a> dot::GraphWalk<'a> for GraphvizDepGraph {
type Node = DepKind;
@@ -303,7 +303,7 @@ impl<'a> dot::Labeller<'a> for GraphvizDepGraph {
fn node_set<'q>(
query: &'q DepGraphQuery,
filter: &DepNodeFilter,
-) -> Option<FxHashSet<&'q DepNode>> {
+) -> Option<FxIndexSet<&'q DepNode>> {
debug!("node_set(filter={:?})", filter);
if filter.accepts_all() {
@@ -315,9 +315,9 @@ fn node_set<'q>(
fn filter_nodes<'q>(
query: &'q DepGraphQuery,
- sources: &Option<FxHashSet<&'q DepNode>>,
- targets: &Option<FxHashSet<&'q DepNode>>,
-) -> FxHashSet<DepKind> {
+ sources: &Option<FxIndexSet<&'q DepNode>>,
+ targets: &Option<FxIndexSet<&'q DepNode>>,
+) -> FxIndexSet<DepKind> {
if let Some(sources) = sources {
if let Some(targets) = targets {
walk_between(query, sources, targets)
@@ -333,10 +333,10 @@ fn filter_nodes<'q>(
fn walk_nodes<'q>(
query: &'q DepGraphQuery,
- starts: &FxHashSet<&'q DepNode>,
+ starts: &FxIndexSet<&'q DepNode>,
direction: Direction,
-) -> FxHashSet<DepKind> {
- let mut set = FxHashSet::default();
+) -> FxIndexSet<DepKind> {
+ let mut set = FxIndexSet::default();
for &start in starts {
debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
if set.insert(start.kind) {
@@ -357,9 +357,9 @@ fn walk_nodes<'q>(
fn walk_between<'q>(
query: &'q DepGraphQuery,
- sources: &FxHashSet<&'q DepNode>,
- targets: &FxHashSet<&'q DepNode>,
-) -> FxHashSet<DepKind> {
+ sources: &FxIndexSet<&'q DepNode>,
+ targets: &FxIndexSet<&'q DepNode>,
+) -> FxIndexSet<DepKind> {
// This is a bit tricky. We want to include a node only if it is:
// (a) reachable from a source and (b) will reach a target. And we
// have to be careful about cycles etc. Luckily efficiency is not
@@ -426,8 +426,8 @@ fn walk_between<'q>(
}
}
-fn filter_edges(query: &DepGraphQuery, nodes: &FxHashSet<DepKind>) -> Vec<(DepKind, DepKind)> {
- let uniq: FxHashSet<_> = query
+fn filter_edges(query: &DepGraphQuery, nodes: &FxIndexSet<DepKind>) -> Vec<(DepKind, DepKind)> {
+ let uniq: FxIndexSet<_> = query
.edges()
.into_iter()
.map(|(s, t)| (s.kind, t.kind))
diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs
index c550e553b..0111a6d30 100644
--- a/compiler/rustc_incremental/src/assert_module_sources.rs
+++ b/compiler/rustc_incremental/src/assert_module_sources.rs
@@ -24,7 +24,7 @@
use crate::errors;
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::unord::UnordSet;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::mir::mono::CodegenUnitNameBuilder;
use rustc_middle::ty::TyCtxt;
@@ -52,7 +52,7 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
struct AssertModuleSource<'tcx> {
tcx: TyCtxt<'tcx>,
- available_cgus: FxHashSet<Symbol>,
+ available_cgus: UnordSet<Symbol>,
}
impl<'tcx> AssertModuleSource<'tcx> {
@@ -118,9 +118,8 @@ impl<'tcx> AssertModuleSource<'tcx> {
debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);
if !self.available_cgus.contains(&cgu_name) {
- let mut cgu_names: Vec<&str> =
- self.available_cgus.iter().map(|cgu| cgu.as_str()).collect();
- cgu_names.sort();
+ let cgu_names: Vec<&str> =
+ self.available_cgus.items().map(|cgu| cgu.as_str()).into_sorted_stable_ord();
self.tcx.sess.emit_err(errors::NoModuleNamed {
span: attr.span,
user_path,
diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs
index 11710c368..b9171fad5 100644
--- a/compiler/rustc_incremental/src/lib.rs
+++ b/compiler/rustc_incremental/src/lib.rs
@@ -4,7 +4,6 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(never_type)]
#![recursion_limit = "256"]
-#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index 43274091c..f9cd01fd8 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -22,6 +22,7 @@
use crate::errors;
use rustc_ast::{self as ast, Attribute, NestedMetaItem};
use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::unord::UnordSet;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit;
use rustc_hir::Node as HirNode;
@@ -125,7 +126,7 @@ const LABELS_ADT: &[&[&str]] = &[BASE_HIR, BASE_STRUCT];
//
// type_of for these.
-type Labels = FxHashSet<String>;
+type Labels = UnordSet<String>;
/// Represents the requested configuration by rustc_clean/dirty
struct Assertion {
@@ -197,7 +198,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
let (name, mut auto) = self.auto_labels(item_id, attr);
let except = self.except(attr);
let loaded_from_disk = self.loaded_from_disk(attr);
- for e in except.iter() {
+ for e in except.items().into_sorted_stable_ord() {
if !auto.remove(e) {
self.tcx.sess.emit_fatal(errors::AssertionAuto { span: attr.span, name, e });
}
@@ -376,15 +377,15 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
continue;
};
self.checked_attrs.insert(attr.id);
- for label in assertion.clean {
+ for label in assertion.clean.items().into_sorted_stable_ord() {
let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
self.assert_clean(item_span, dep_node);
}
- for label in assertion.dirty {
+ for label in assertion.dirty.items().into_sorted_stable_ord() {
let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
self.assert_dirty(item_span, dep_node);
}
- for label in assertion.loaded_from_disk {
+ for label in assertion.loaded_from_disk.items().into_sorted_stable_ord() {
let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
self.assert_loaded_from_disk(item_span, dep_node);
}
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index e3c688b3e..7708deece 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -104,8 +104,9 @@
//! implemented.
use crate::errors;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_data_structures::svh::Svh;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_data_structures::{base_n, flock};
use rustc_errors::ErrorGuaranteed;
use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy};
@@ -635,8 +636,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
// First do a pass over the crate directory, collecting lock files and
// session directories
- let mut session_directories = FxHashSet::default();
- let mut lock_files = FxHashSet::default();
+ let mut session_directories = FxIndexSet::default();
+ let mut lock_files = UnordSet::default();
for dir_entry in crate_directory.read_dir()? {
let Ok(dir_entry) = dir_entry else {
@@ -657,10 +658,11 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
// This is something we don't know, leave it alone
}
}
+ session_directories.sort();
// Now map from lock files to session directories
- let lock_file_to_session_dir: FxHashMap<String, Option<String>> = lock_files
- .into_iter()
+ let lock_file_to_session_dir: UnordMap<String, Option<String>> = lock_files
+ .into_items()
.map(|lock_file_name| {
assert!(lock_file_name.ends_with(LOCK_FILE_EXT));
let dir_prefix_end = lock_file_name.len() - LOCK_FILE_EXT.len();
@@ -670,11 +672,13 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
};
(lock_file_name, session_dir.map(String::clone))
})
- .collect();
+ .into();
// Delete all lock files, that don't have an associated directory. They must
// be some kind of leftover
- for (lock_file_name, directory_name) in &lock_file_to_session_dir {
+ for (lock_file_name, directory_name) in
+ lock_file_to_session_dir.items().into_sorted_stable_ord()
+ {
if directory_name.is_none() {
let Ok(timestamp) = extract_timestamp_from_session_dir(lock_file_name) else {
debug!(
@@ -685,19 +689,19 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
continue;
};
- let lock_file_path = crate_directory.join(&**lock_file_name);
+ let lock_file_path = crate_directory.join(&*lock_file_name);
if is_old_enough_to_be_collected(timestamp) {
debug!(
"garbage_collect_session_directories() - deleting \
- garbage lock file: {}",
+ garbage lock file: {}",
lock_file_path.display()
);
delete_session_dir_lock_file(sess, &lock_file_path);
} else {
debug!(
"garbage_collect_session_directories() - lock file with \
- no session dir not old enough to be collected: {}",
+ no session dir not old enough to be collected: {}",
lock_file_path.display()
);
}
@@ -705,14 +709,14 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
}
// Filter out `None` directories
- let lock_file_to_session_dir: FxHashMap<String, String> = lock_file_to_session_dir
- .into_iter()
+ let lock_file_to_session_dir: UnordMap<String, String> = lock_file_to_session_dir
+ .into_items()
.filter_map(|(lock_file_name, directory_name)| directory_name.map(|n| (lock_file_name, n)))
- .collect();
+ .into();
// Delete all session directories that don't have a lock file.
for directory_name in session_directories {
- if !lock_file_to_session_dir.values().any(|dir| *dir == directory_name) {
+ if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) {
let path = crate_directory.join(directory_name);
if let Err(err) = safe_remove_dir_all(&path) {
sess.emit_warning(errors::InvalidGcFailed { path: &path, err });
@@ -721,103 +725,103 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
}
// Now garbage collect the valid session directories.
- let mut deletion_candidates = vec![];
+ let deletion_candidates =
+ lock_file_to_session_dir.items().filter_map(|(lock_file_name, directory_name)| {
+ debug!("garbage_collect_session_directories() - inspecting: {}", directory_name);
- for (lock_file_name, directory_name) in &lock_file_to_session_dir {
- debug!("garbage_collect_session_directories() - inspecting: {}", directory_name);
-
- let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else {
+ let Ok(timestamp) = extract_timestamp_from_session_dir(directory_name) else {
debug!(
"found session-dir with malformed timestamp: {}",
crate_directory.join(directory_name).display()
);
// Ignore it
- continue;
+ return None;
};
- if is_finalized(directory_name) {
- let lock_file_path = crate_directory.join(lock_file_name);
- match flock::Lock::new(
- &lock_file_path,
- false, // don't wait
- false, // don't create the lock-file
- true,
- ) {
- // get an exclusive lock
- Ok(lock) => {
- debug!(
- "garbage_collect_session_directories() - \
+ if is_finalized(directory_name) {
+ let lock_file_path = crate_directory.join(lock_file_name);
+ match flock::Lock::new(
+ &lock_file_path,
+ false, // don't wait
+ false, // don't create the lock-file
+ true,
+ ) {
+ // get an exclusive lock
+ Ok(lock) => {
+ debug!(
+ "garbage_collect_session_directories() - \
successfully acquired lock"
- );
- debug!(
- "garbage_collect_session_directories() - adding \
+ );
+ debug!(
+ "garbage_collect_session_directories() - adding \
deletion candidate: {}",
- directory_name
- );
-
- // Note that we are holding on to the lock
- deletion_candidates.push((
- timestamp,
- crate_directory.join(directory_name),
- Some(lock),
- ));
- }
- Err(_) => {
- debug!(
- "garbage_collect_session_directories() - \
+ directory_name
+ );
+
+ // Note that we are holding on to the lock
+ return Some((
+ (timestamp, crate_directory.join(directory_name)),
+ Some(lock),
+ ));
+ }
+ Err(_) => {
+ debug!(
+ "garbage_collect_session_directories() - \
not collecting, still in use"
- );
+ );
+ }
}
- }
- } else if is_old_enough_to_be_collected(timestamp) {
- // When cleaning out "-working" session directories, i.e.
- // session directories that might still be in use by another
- // compiler instance, we only look a directories that are
- // at least ten seconds old. This is supposed to reduce the
- // chance of deleting a directory in the time window where
- // the process has allocated the directory but has not yet
- // acquired the file-lock on it.
-
- // Try to acquire the directory lock. If we can't, it
- // means that the owning process is still alive and we
- // leave this directory alone.
- let lock_file_path = crate_directory.join(lock_file_name);
- match flock::Lock::new(
- &lock_file_path,
- false, // don't wait
- false, // don't create the lock-file
- true,
- ) {
- // get an exclusive lock
- Ok(lock) => {
- debug!(
- "garbage_collect_session_directories() - \
+ } else if is_old_enough_to_be_collected(timestamp) {
+ // When cleaning out "-working" session directories, i.e.
+ // session directories that might still be in use by another
+ // compiler instance, we only look a directories that are
+ // at least ten seconds old. This is supposed to reduce the
+ // chance of deleting a directory in the time window where
+ // the process has allocated the directory but has not yet
+ // acquired the file-lock on it.
+
+ // Try to acquire the directory lock. If we can't, it
+ // means that the owning process is still alive and we
+ // leave this directory alone.
+ let lock_file_path = crate_directory.join(lock_file_name);
+ match flock::Lock::new(
+ &lock_file_path,
+ false, // don't wait
+ false, // don't create the lock-file
+ true,
+ ) {
+ // get an exclusive lock
+ Ok(lock) => {
+ debug!(
+ "garbage_collect_session_directories() - \
successfully acquired lock"
- );
+ );
- delete_old(sess, &crate_directory.join(directory_name));
+ 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
- drop(lock);
- }
- Err(_) => {
- debug!(
- "garbage_collect_session_directories() - \
+ // Let's make it explicit that the file lock is released at this point,
+ // or rather, that we held on to it until here
+ drop(lock);
+ }
+ Err(_) => {
+ debug!(
+ "garbage_collect_session_directories() - \
not collecting, still in use"
- );
+ );
+ }
}
- }
- } else {
- debug!(
- "garbage_collect_session_directories() - not finalized, not \
+ } else {
+ debug!(
+ "garbage_collect_session_directories() - not finalized, not \
old enough"
- );
- }
- }
+ );
+ }
+ None
+ });
+ let deletion_candidates = deletion_candidates.into();
// Delete all but the most recent of the candidates
- for (path, lock) in all_except_most_recent(deletion_candidates) {
+ all_except_most_recent(deletion_candidates).into_items().all(|(path, lock)| {
debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
if let Err(err) = safe_remove_dir_all(&path) {
@@ -829,7 +833,8 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> {
// Let's make it explicit that the file lock is released at this point,
// or rather, that we held on to it until here
drop(lock);
- }
+ true
+ });
Ok(())
}
@@ -845,18 +850,18 @@ fn delete_old(sess: &Session, path: &Path) {
}
fn all_except_most_recent(
- deletion_candidates: Vec<(SystemTime, PathBuf, Option<flock::Lock>)>,
-) -> FxHashMap<PathBuf, Option<flock::Lock>> {
- let most_recent = deletion_candidates.iter().map(|&(timestamp, ..)| timestamp).max();
+ deletion_candidates: UnordMap<(SystemTime, PathBuf), Option<flock::Lock>>,
+) -> UnordMap<PathBuf, Option<flock::Lock>> {
+ let most_recent = deletion_candidates.items().map(|(&(timestamp, _), _)| timestamp).max();
if let Some(most_recent) = most_recent {
deletion_candidates
- .into_iter()
- .filter(|&(timestamp, ..)| timestamp != most_recent)
- .map(|(_, path, lock)| (path, lock))
+ .into_items()
+ .filter(|&((timestamp, _), _)| timestamp != most_recent)
+ .map(|((_, path), lock)| (path, lock))
.collect()
} else {
- FxHashMap::default()
+ UnordMap::default()
}
}
diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs
index 184796948..644b81876 100644
--- a/compiler/rustc_incremental/src/persist/fs/tests.rs
+++ b/compiler/rustc_incremental/src/persist/fs/tests.rs
@@ -2,26 +2,19 @@ use super::*;
#[test]
fn test_all_except_most_recent() {
+ let input: UnordMap<_, Option<flock::Lock>> = UnordMap::from_iter([
+ ((UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4")), None),
+ ((UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1")), None),
+ ((UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5")), None),
+ ((UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3")), None),
+ ((UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2")), None),
+ ]);
assert_eq!(
- all_except_most_recent(vec![
- (UNIX_EPOCH + Duration::new(4, 0), PathBuf::from("4"), None),
- (UNIX_EPOCH + Duration::new(1, 0), PathBuf::from("1"), None),
- (UNIX_EPOCH + Duration::new(5, 0), PathBuf::from("5"), None),
- (UNIX_EPOCH + Duration::new(3, 0), PathBuf::from("3"), None),
- (UNIX_EPOCH + Duration::new(2, 0), PathBuf::from("2"), None),
- ])
- .keys()
- .cloned()
- .collect::<FxHashSet<PathBuf>>(),
- [PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4"),]
- .into_iter()
- .collect::<FxHashSet<PathBuf>>()
+ all_except_most_recent(input).into_items().map(|(path, _)| path).into_sorted_stable_ord(),
+ vec![PathBuf::from("1"), PathBuf::from("2"), PathBuf::from("3"), PathBuf::from("4")]
);
- assert_eq!(
- all_except_most_recent(vec![]).keys().cloned().collect::<FxHashSet<PathBuf>>(),
- FxHashSet::default()
- );
+ assert!(all_except_most_recent(UnordMap::default()).is_empty());
}
#[test]
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index a4407a93f..bb479b5bd 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -1,8 +1,8 @@
//! Code to save/load the dep-graph from files.
use crate::errors;
-use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::memmap::Mmap;
+use rustc_data_structures::unord::UnordMap;
use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId};
use rustc_middle::query::on_disk_cache::OnDiskCache;
use rustc_serialize::opaque::MemDecoder;
@@ -16,7 +16,7 @@ use super::file_format;
use super::fs::*;
use super::work_product;
-type WorkProductMap = FxHashMap<WorkProductId, WorkProduct>;
+type WorkProductMap = UnordMap<WorkProductId, WorkProduct>;
#[derive(Debug)]
/// Represents the result of an attempt to load incremental compilation data.
@@ -147,7 +147,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
let report_incremental_info = sess.opts.unstable_opts.incremental_info;
let expected_hash = sess.opts.dep_tracking_hash(false);
- let mut prev_work_products = FxHashMap::default();
+ let mut prev_work_products = UnordMap::default();
// If we are only building with -Zquery-dep-graph but without an actual
// incr. comp. session directory, we skip this. Otherwise we'd fail
@@ -163,7 +163,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
Decodable::decode(&mut work_product_decoder);
for swp in work_products {
- let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| {
+ let all_files_exist = swp.work_product.saved_files.items().all(|(_, path)| {
let exists = in_incr_comp_dir_sess(sess, path).exists();
if !exists && sess.opts.unstable_opts.incremental_info {
eprintln!("incremental: could not find file for work product: {path}",);
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 7376be6be..bfaa52f9c 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -1,5 +1,5 @@
use crate::errors;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::join;
use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId};
use rustc_middle::ty::TyCtxt;
@@ -79,7 +79,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
pub fn save_work_product_index(
sess: &Session,
dep_graph: &DepGraph,
- new_work_products: FxHashMap<WorkProductId, WorkProduct>,
+ new_work_products: FxIndexMap<WorkProductId, WorkProduct>,
) {
if sess.opts.incremental.is_none() {
return;
@@ -105,7 +105,7 @@ pub fn save_work_product_index(
if !new_work_products.contains_key(id) {
work_product::delete_workproduct_files(sess, wp);
debug_assert!(
- !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
+ !wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
);
}
}
@@ -113,13 +113,13 @@ pub fn save_work_product_index(
// Check that we did not delete one of the current work-products:
debug_assert!({
new_work_products.iter().all(|(_, wp)| {
- wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
+ wp.saved_files.items().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists())
})
});
}
fn encode_work_product_index(
- work_products: &FxHashMap<WorkProductId, WorkProduct>,
+ work_products: &FxIndexMap<WorkProductId, WorkProduct>,
encoder: &mut FileEncoder,
) {
let serialized_products: Vec<_> = work_products
@@ -146,7 +146,7 @@ fn encode_query_cache(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult
pub fn build_dep_graph(
sess: &Session,
prev_graph: SerializedDepGraph,
- prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
+ prev_work_products: FxIndexMap<WorkProductId, WorkProduct>,
) -> Option<DepGraph> {
if sess.opts.incremental.is_none() {
// No incremental compilation.
diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs
index dc98fbeb0..bce5ca1e1 100644
--- a/compiler/rustc_incremental/src/persist/work_product.rs
+++ b/compiler/rustc_incremental/src/persist/work_product.rs
@@ -4,7 +4,7 @@
use crate::errors;
use crate::persist::fs::*;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordMap;
use rustc_fs_util::link_or_copy;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_session::Session;
@@ -20,7 +20,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
debug!(?cgu_name, ?files);
sess.opts.incremental.as_ref()?;
- let mut saved_files = FxHashMap::default();
+ let mut saved_files = UnordMap::default();
for (ext, path) in files {
let file_name = format!("{cgu_name}.{ext}");
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
@@ -46,7 +46,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
/// Removes files for a given work product.
pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
- for (_, path) in &work_product.saved_files {
+ for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() {
let path = in_incr_comp_dir_sess(sess, path);
if let Err(err) = std_fs::remove_file(&path) {
sess.emit_warning(errors::DeleteWorkProduct { path: &path, err });
diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl
index f44c4a7c1..4d0e77063 100644
--- a/compiler/rustc_infer/messages.ftl
+++ b/compiler/rustc_infer/messages.ftl
@@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type
infer_ril_introduced_here = `'static` requirement introduced here
infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
-infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
-infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
-
infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index b1e819e83..7e1fa08f2 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1247,30 +1247,6 @@ pub struct FnConsiderCasting {
}
#[derive(Subdiagnostic)]
-pub enum SuggestAsRefWhereAppropriate<'a> {
- #[suggestion(
- infer_sarwa_option,
- code = "{snippet}.as_ref()",
- applicability = "machine-applicable"
- )]
- Option {
- #[primary_span]
- span: Span,
- snippet: &'a str,
- },
- #[suggestion(
- infer_sarwa_result,
- code = "{snippet}.as_ref()",
- applicability = "machine-applicable"
- )]
- Result {
- #[primary_span]
- span: Span,
- snippet: &'a str,
- },
-}
-
-#[derive(Subdiagnostic)]
pub enum SuggestAccessingField<'a> {
#[suggestion(
infer_suggest_accessing_field,
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 0c8854e96..433735e82 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -40,6 +40,7 @@ pub enum DefineOpaqueTypes {
No,
}
+#[derive(Clone, Copy)]
pub struct At<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub cause: &'a ObligationCause<'tcx>,
@@ -70,8 +71,8 @@ impl<'tcx> InferCtxt<'tcx> {
tcx: self.tcx,
defining_use_anchor: self.defining_use_anchor,
considering_regions: self.considering_regions,
+ skip_leak_check: self.skip_leak_check,
inner: self.inner.clone(),
- skip_leak_check: self.skip_leak_check.clone(),
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
selection_cache: self.selection_cache.clone(),
evaluation_cache: self.evaluation_cache.clone(),
@@ -79,9 +80,9 @@ impl<'tcx> InferCtxt<'tcx> {
reported_closure_mismatch: self.reported_closure_mismatch.clone(),
tainted_by_errors: self.tainted_by_errors.clone(),
err_count_on_creation: self.err_count_on_creation,
- in_snapshot: self.in_snapshot.clone(),
universe: self.universe.clone(),
intercrate: self.intercrate,
+ next_trait_solver: self.next_trait_solver,
}
}
}
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 427d05c8b..e57532e2d 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -382,7 +382,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
// any equated inference vars correctly!
let root_vid = self.infcx.root_var(vid);
if root_vid != vid {
- t = self.infcx.tcx.mk_ty_var(root_vid);
+ t = Ty::new_var(self.infcx.tcx, root_vid);
vid = root_vid;
}
@@ -497,7 +497,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
// any equated inference vars correctly!
let root_vid = self.infcx.root_const_var(vid);
if root_vid != vid {
- ct = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), ct.ty());
+ ct = ty::Const::new_var(self.infcx.tcx, root_vid, ct.ty());
vid = root_vid;
}
@@ -771,7 +771,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
) -> ty::Region<'tcx> {
let var = self.canonical_var(info, r.into());
let br = ty::BoundRegion { var, kind: ty::BrAnon(None) };
- self.interner().mk_re_late_bound(self.binder_index, br)
+ ty::Region::new_late_bound(self.interner(), self.binder_index, br)
}
/// Given a type variable `ty_var` of the given kind, first check
@@ -785,7 +785,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
self.fold_ty(bound_to)
} else {
let var = self.canonical_var(info, ty_var.into());
- self.interner().mk_bound(self.binder_index, var.into())
+ Ty::new_bound(self.tcx, self.binder_index, var.into())
}
}
@@ -804,10 +804,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
self.fold_const(bound_to)
} else {
let var = self.canonical_var(info, const_var.into());
- self.interner().mk_const(
- ty::ConstKind::Bound(self.binder_index, var),
- self.fold_ty(const_var.ty()),
- )
+ ty::Const::new_bound(self.tcx, self.binder_index, var, self.fold_ty(const_var.ty()))
}
}
}
diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs
index 2abdd5b0a..f765c41a3 100644
--- a/compiler/rustc_infer/src/infer/canonical/mod.rs
+++ b/compiler/rustc_infer/src/infer/canonical/mod.rs
@@ -26,7 +26,7 @@ use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVari
use rustc_index::IndexVec;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{self, List, TyCtxt};
+use rustc_middle::ty::{self, List, Ty, TyCtxt};
use rustc_span::source_map::Span;
pub use rustc_middle::infer::canonical::*;
@@ -128,7 +128,7 @@ impl<'tcx> InferCtxt<'tcx> {
CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, bound }) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, bound };
- self.tcx.mk_placeholder(placeholder_mapped).into()
+ Ty::new_placeholder(self.tcx, placeholder_mapped).into()
}
CanonicalVarKind::Region(ui) => self
@@ -141,7 +141,7 @@ impl<'tcx> InferCtxt<'tcx> {
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion { universe, bound }) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderRegion { universe: universe_mapped, bound };
- self.tcx.mk_re_placeholder(placeholder_mapped).into()
+ ty::Region::new_placeholder(self.tcx, placeholder_mapped).into()
}
CanonicalVarKind::Const(ui, ty) => self
@@ -155,7 +155,7 @@ impl<'tcx> InferCtxt<'tcx> {
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, bound }, ty) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, bound };
- self.tcx.mk_const(placeholder_mapped, ty).into()
+ ty::Const::new_placeholder(self.tcx, placeholder_mapped, ty).into()
}
}
}
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 88256c819..9c3ab04de 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -520,7 +520,7 @@ impl<'tcx> InferCtxt<'tcx> {
self.at(cause, param_env)
.eq(
DefineOpaqueTypes::Yes,
- self.tcx.mk_opaque(a.def_id.to_def_id(), a.substs),
+ Ty::new_opaque(self.tcx, a.def_id.to_def_id(), a.substs),
b,
)?
.obligations,
@@ -584,12 +584,12 @@ impl<'tcx> InferCtxt<'tcx> {
let ty::OutlivesPredicate(k1, r2) = predicate;
let atom = match k1.unpack() {
- GenericArgKind::Lifetime(r1) => {
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(r1, r2)))
- }
- GenericArgKind::Type(t1) => {
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(t1, r2)))
- }
+ GenericArgKind::Lifetime(r1) => ty::PredicateKind::Clause(
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)),
+ ),
+ GenericArgKind::Type(t1) => ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(
+ ty::OutlivesPredicate(t1, r2),
+ )),
GenericArgKind::Const(..) => {
// Consts cannot outlive one another, so we don't expect to
// encounter this branch.
@@ -668,14 +668,15 @@ pub fn make_query_region_constraints<'tcx>(
let constraint = match *k {
// Swap regions because we are going from sub (<=) to outlives
// (>=).
- Constraint::VarSubVar(v1, v2) => {
- ty::OutlivesPredicate(tcx.mk_re_var(v2).into(), tcx.mk_re_var(v1))
- }
+ Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate(
+ ty::Region::new_var(tcx, v2).into(),
+ ty::Region::new_var(tcx, v1),
+ ),
Constraint::VarSubReg(v1, r2) => {
- ty::OutlivesPredicate(r2.into(), tcx.mk_re_var(v1))
+ ty::OutlivesPredicate(r2.into(), ty::Region::new_var(tcx, v1))
}
Constraint::RegSubVar(r1, v2) => {
- ty::OutlivesPredicate(tcx.mk_re_var(v2).into(), r1)
+ ty::OutlivesPredicate(ty::Region::new_var(tcx, v2).into(), r1)
}
Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1),
};
@@ -719,7 +720,7 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
}
fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> {
- self.infcx.tcx.mk_re_placeholder(placeholder)
+ ty::Region::new_placeholder(self.infcx.tcx, placeholder)
}
fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
@@ -738,10 +739,8 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
self.obligations.push(Obligation {
cause: self.cause.clone(),
param_env: self.param_env,
- predicate: ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
- ty::OutlivesPredicate(sup, sub),
- )))
- .to_predicate(self.infcx.tcx),
+ predicate: ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(sup, sub))
+ .to_predicate(self.infcx.tcx),
recursion_depth: 0,
});
}
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index b6b935de6..a9cdb8c51 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -34,7 +34,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
-use rustc_middle::ty::{self, AliasKind, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{IntType, UintType};
use rustc_span::DUMMY_SP;
@@ -103,17 +103,17 @@ impl<'tcx> InferCtxt<'tcx> {
// We don't expect `TyVar` or `Fresh*` vars at this point with lazy norm.
(
- ty::Alias(AliasKind::Projection, _),
+ ty::Alias(..),
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
)
| (
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
- ty::Alias(AliasKind::Projection, _),
- ) if self.tcx.trait_solver_next() => {
+ ty::Alias(..),
+ ) if self.next_trait_solver() => {
bug!()
}
- (_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => {
+ (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
relation.register_type_relate_obligation(a, b);
Ok(a)
}
@@ -124,13 +124,10 @@ impl<'tcx> InferCtxt<'tcx> {
}
// During coherence, opaque types should be treated as *possibly*
- // equal to each other, even if their generic params differ, as
- // they could resolve to the same hidden type, even for different
- // generic params.
- (
- &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
- &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
- ) if self.intercrate && a_def_id == b_def_id => {
+ // equal to any other type (except for possibly itself). This is an
+ // extremely heavy hammer, but can be relaxed in a fowards-compatible
+ // way later.
+ (&ty::Alias(ty::Opaque, _), _) | (_, &ty::Alias(ty::Opaque, _)) if self.intercrate => {
relation.register_predicates([ty::Binder::dummy(ty::PredicateKind::Ambiguous)]);
Ok(a)
}
@@ -192,11 +189,11 @@ impl<'tcx> InferCtxt<'tcx> {
// HACK: equating both sides with `[const error]` eagerly prevents us
// from leaving unconstrained inference vars during things like impl
// matching in the solver.
- let a_error = self.tcx.const_error(a.ty(), guar);
+ let a_error = ty::Const::new_error(self.tcx, guar, a.ty());
if let ty::ConstKind::Infer(InferConst::Var(vid)) = a.kind() {
return self.unify_const_variable(vid, a_error, relation.param_env());
}
- let b_error = self.tcx.const_error(b.ty(), guar);
+ let b_error = ty::Const::new_error(self.tcx, guar, b.ty());
if let ty::ConstKind::Infer(InferConst::Var(vid)) = b.kind() {
return self.unify_const_variable(vid, b_error, relation.param_env());
}
@@ -227,9 +224,20 @@ impl<'tcx> InferCtxt<'tcx> {
return self.unify_const_variable(vid, a, relation.param_env());
}
(ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
- if self.tcx.lazy_normalization() =>
+ if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
{
- relation.register_const_equate_obligation(a, b);
+ let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
+
+ relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() {
+ ty::PredicateKind::AliasRelate(
+ a.into(),
+ b.into(),
+ ty::AliasRelationDirection::Equate,
+ )
+ } else {
+ ty::PredicateKind::ConstEquate(a, b)
+ })]);
+
return Ok(b);
}
_ => {}
@@ -314,8 +322,8 @@ impl<'tcx> InferCtxt<'tcx> {
.unify_var_value(vid, Some(val))
.map_err(|e| int_unification_error(vid_is_expected, e))?;
match val {
- IntType(v) => Ok(self.tcx.mk_mach_int(v)),
- UintType(v) => Ok(self.tcx.mk_mach_uint(v)),
+ IntType(v) => Ok(Ty::new_int(self.tcx, v)),
+ UintType(v) => Ok(Ty::new_uint(self.tcx, v)),
}
}
@@ -330,7 +338,7 @@ impl<'tcx> InferCtxt<'tcx> {
.float_unification_table()
.unify_var_value(vid, Some(ty::FloatVarValue(val)))
.map_err(|e| float_unification_error(vid_is_expected, e))?;
- Ok(self.tcx.mk_mach_float(val))
+ Ok(Ty::new_float(self.tcx, val))
}
}
@@ -406,7 +414,9 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
self.tcx(),
self.trace.cause.clone(),
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(b_ty.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ b_ty.into(),
+ ))),
));
}
@@ -453,19 +463,6 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
/// be used if control over the obligation causes is required.
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>);
- /// Register an obligation that both constants must be equal to each other.
- ///
- /// If they aren't equal then the relation doesn't hold.
- fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
- let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
-
- self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() {
- ty::PredicateKind::AliasRelate(a.into(), b.into(), ty::AliasRelationDirection::Equate)
- } else {
- ty::PredicateKind::ConstEquate(a, b)
- })]);
- }
-
/// Register an obligation that both types must be related to each other according to
/// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`]
fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
index 42dfe4f6b..495c250a7 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -105,7 +105,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local()
- && !self.tcx().trait_solver_next() =>
+ && !self.fields.infcx.next_trait_solver() =>
{
self.fields.obligations.extend(
infcx
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 35c05e80b..b826ced04 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -76,6 +76,7 @@ use rustc_middle::ty::{
};
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
+use std::borrow::Cow;
use std::ops::{ControlFlow, Deref};
use std::path::PathBuf;
use std::{cmp, fmt, iter};
@@ -314,7 +315,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let mut err = tcx.sess.create_err(errors::OpaqueCapturesLifetime {
span,
- opaque_ty: tcx.mk_opaque(opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs),
+ opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.substs),
opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
});
@@ -407,7 +408,7 @@ impl<'tcx> InferCtxt<'tcx> {
predicate
.kind()
.map_bound(|kind| match kind {
- ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate))
+ ty::ClauseKind::Projection(projection_predicate)
if projection_predicate.projection_ty.def_id == item_def_id =>
{
projection_predicate.term.ty()
@@ -846,7 +847,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
) {
err.subdiagnostic(subdiag);
}
- if let Some(ret_sp) = opt_suggest_box_span {
+ // don't suggest wrapping either blocks in `if .. {} else {}`
+ let is_empty_arm = |id| {
+ let hir::Node::Block(blk) = self.tcx.hir().get(id)
+ else {
+ return false;
+ };
+ if blk.expr.is_some() || !blk.stmts.is_empty() {
+ return false;
+ }
+ let Some((_, hir::Node::Expr(expr))) = self.tcx.hir().parent_iter(id).nth(1)
+ else {
+ return false;
+ };
+ matches!(expr.kind, hir::ExprKind::If(..))
+ };
+ if let Some(ret_sp) = opt_suggest_box_span
+ && !is_empty_arm(then_id)
+ && !is_empty_arm(else_id)
+ {
self.suggest_boxing_for_return_impl_trait(
err,
ret_sp,
@@ -1470,7 +1489,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&self,
diag: &mut Diagnostic,
cause: &ObligationCause<'tcx>,
- secondary_span: Option<(Span, String)>,
+ secondary_span: Option<(Span, Cow<'static, str>)>,
mut values: Option<ValuePairs<'tcx>>,
terr: TypeError<'tcx>,
swap_secondary_and_primary: bool,
@@ -1629,7 +1648,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
};
- let mut label_or_note = |span: Span, msg: &str| {
+ let mut label_or_note = |span: Span, msg: Cow<'static, str>| {
if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
diag.span_label(span, msg);
} else {
@@ -1643,15 +1662,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
..
})) = values
{
- format!("expected this to be `{}`", expected)
+ Cow::from(format!("expected this to be `{}`", expected))
} else {
- terr.to_string(self.tcx).to_string()
+ terr.to_string(self.tcx)
};
- label_or_note(sp, &terr);
- label_or_note(span, &msg);
+ label_or_note(sp, terr);
+ label_or_note(span, msg);
} else {
- label_or_note(span, &terr.to_string(self.tcx));
- label_or_note(sp, &msg);
+ label_or_note(span, terr.to_string(self.tcx));
+ label_or_note(sp, msg);
}
} else {
if let Some(values) = values
@@ -1663,12 +1682,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
if expected == found {
- label_or_note(span, &terr.to_string(self.tcx));
+ label_or_note(span, terr.to_string(self.tcx));
} else {
- label_or_note(span, &format!("expected {expected}, found {found}"));
+ label_or_note(span, Cow::from(format!("expected {expected}, found {found}")));
}
} else {
- label_or_note(span, &terr.to_string(self.tcx));
+ label_or_note(span, terr.to_string(self.tcx));
}
}
@@ -1896,7 +1915,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if should_suggest_fixes {
self.suggest_tuple_pattern(cause, &exp_found, diag);
- self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
self.suggest_function_pointers(cause, span, &exp_found, diag);
@@ -2357,6 +2375,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
ty::AliasKind::Projection | ty::AliasKind::Inherent => {
format!("the associated type `{}`", p)
}
+ ty::AliasKind::Weak => format!("the type alias `{}`", p),
ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
},
};
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 f3b2ec4c5..bb75ecc6a 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
@@ -265,9 +265,9 @@ impl<'tcx> InferCtxt<'tcx> {
kind: UnderspecifiedArgKind::Type {
prefix: "type parameter".into(),
},
- parent: def_id.and_then(|def_id| {
- InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id)
- }),
+ parent: InferenceDiagnosticsParentData::for_def_id(
+ self.tcx, def_id,
+ ),
};
}
}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index c9c1f0aea..0b3bc1ce6 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -79,7 +79,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_re_var(*vid)),
+ Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
Some(*sub_placeholder),
Some(*sup_placeholder),
@@ -95,7 +95,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
_,
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_re_var(*vid)),
+ Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
Some(*sub_placeholder),
None,
@@ -111,7 +111,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_re_var(*vid)),
+ Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
@@ -127,7 +127,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_re_var(*vid)),
+ Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
@@ -141,7 +141,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_re_var(*vid)),
+ Some(ty::Region::new_var(self.tcx(), *vid)),
cause,
None,
Some(*sup_placeholder),
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 aad988582..a9b485a6f 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
@@ -288,7 +288,7 @@ pub fn suggest_new_region_bound(
// Get the identity type for this RPIT
let did = item_id.owner_id.to_def_id();
- let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did));
+ let ty = Ty::new_opaque(tcx, did, ty::InternalSubsts::identity_for_item(tcx, did));
if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
GenericBound::Outlives(Lifetime {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
index ce70bcc5c..12d38ced0 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -41,8 +41,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
// all of the region highlighting machinery only deals with those.
let guar = self.emit_err(
var_origin.span(),
- self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(expected)),
- self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(found)),
+ Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(expected)),
+ Ty::new_fn_ptr(self.cx.tcx,ty::Binder::dummy(found)),
*trait_item_def_id,
);
return Some(guar);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 07a9eff2d..e55e9e75f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -11,7 +11,7 @@ use rustc_errors::{
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, IsSuggestable, Region};
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
use rustc_span::symbol::kw;
use super::ObligationCauseAsDiagArg;
@@ -304,7 +304,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let trait_substs = trait_ref
.subst_identity()
// Replace the explicit self type with `Self` for better suggestion rendering
- .with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
+ .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
.substs;
let trait_item_substs = ty::InternalSubsts::identity_for_item(self.tcx, impl_item_def_id)
.rebase_onto(self.tcx, impl_def_id, trait_substs);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 421eb807a..63613b590 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -139,7 +139,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
tcx,
generics,
diag,
- &format!("{}", proj.self_ty()),
+ &proj.self_ty().to_string(),
&path,
None,
matching_span,
@@ -153,7 +153,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
tcx,
generics,
diag,
- &format!("{}", proj.self_ty()),
+ &proj.self_ty().to_string(),
&path,
None,
matching_span,
@@ -234,13 +234,13 @@ impl<T> Trait<T> for X {
);
}
(_, ty::Alias(ty::Projection | ty::Inherent, proj_ty)) if !tcx.is_impl_trait_in_trait(proj_ty.def_id) => {
- let msg = format!(
+ let msg = || format!(
"consider constraining the associated type `{}` to `{}`",
values.found, values.expected,
);
if !(self.suggest_constraining_opaque_associated_type(
diag,
- &msg,
+ msg,
proj_ty,
values.expected,
) || self.suggest_constraint(
@@ -250,17 +250,18 @@ impl<T> Trait<T> for X {
proj_ty,
values.expected,
)) {
- diag.help(msg);
+ diag.help(msg());
diag.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
);
}
}
- (ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias)) if alias.def_id.is_local() && matches!(tcx.def_kind(body_owner_def_id), DefKind::AssocFn | DefKind::AssocConst) => {
+ (ty::Alias(ty::Opaque, alias), _) | (_, ty::Alias(ty::Opaque, alias)) if alias.def_id.is_local() && matches!(tcx.def_kind(body_owner_def_id), DefKind::Fn | DefKind::Static(_) | DefKind::Const | DefKind::AssocFn | DefKind::AssocConst) => {
if tcx.is_type_alias_impl_trait(alias.def_id) {
if !tcx.opaque_types_defined_by(body_owner_def_id.expect_local()).contains(&alias.def_id.expect_local()) {
- diag.span_note(tcx.def_span(body_owner_def_id), "\
+ let sp = tcx.def_ident_span(body_owner_def_id).unwrap_or_else(|| tcx.def_span(body_owner_def_id));
+ diag.span_note(sp, "\
this item must have the opaque type in its signature \
in order to be able to register hidden types");
}
@@ -308,7 +309,7 @@ impl<T> Trait<T> for X {
fn suggest_constraint(
&self,
diag: &mut Diagnostic,
- msg: &str,
+ msg: impl Fn() -> String,
body_owner_def_id: DefId,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
@@ -340,7 +341,7 @@ impl<T> Trait<T> for X {
assoc,
assoc_substs,
ty,
- msg,
+ &msg,
false,
) {
return true;
@@ -374,10 +375,18 @@ impl<T> Trait<T> for X {
) {
let tcx = self.tcx;
- let msg = format!(
- "consider constraining the associated type `{}` to `{}`",
- values.expected, values.found
- );
+ // Don't suggest constraining a projection to something containing itself
+ if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
+ return;
+ }
+
+ let msg = || {
+ format!(
+ "consider constraining the associated type `{}` to `{}`",
+ values.expected, values.found
+ )
+ };
+
let body_owner = tcx.hir().get_if_local(body_owner_def_id);
let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
@@ -428,10 +437,11 @@ impl<T> Trait<T> for X {
if callable_scope {
diag.help(format!(
"{} or calling a method that returns `{}`",
- msg, values.expected
+ msg(),
+ values.expected
));
} else {
- diag.help(msg);
+ diag.help(msg());
}
diag.note(
"for more information, visit \
@@ -463,7 +473,7 @@ fn foo(&self) -> Self::T { String::new() }
fn suggest_constraining_opaque_associated_type(
&self,
diag: &mut Diagnostic,
- msg: &str,
+ msg: impl Fn() -> String,
proj_ty: &ty::AliasTy<'tcx>,
ty: Ty<'tcx>,
) -> bool {
@@ -564,7 +574,9 @@ fn foo(&self) -> Self::T { String::new() }
let Some(hir_id) = body_owner_def_id.as_local() else {
return false;
};
- let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id);
+ let Some(hir_id) = tcx.opt_local_def_id_to_hir_id(hir_id) else {
+ return false;
+ };
// When `body_owner` is an `impl` or `trait` item, look in its associated types for
// `expected` and point at it.
let parent_id = tcx.hir().get_parent_item(hir_id);
@@ -583,7 +595,7 @@ fn foo(&self) -> Self::T { String::new() }
// FIXME: account for returning some type in a trait fn impl that has
// an assoc type as a return type (#72076).
if let hir::Defaultness::Default { has_value: true } =
- tcx.impl_defaultness(item.id.owner_id)
+ tcx.defaultness(item.id.owner_id)
{
let assoc_ty = tcx.type_of(item.id.owner_id).subst_identity();
if self.infcx.can_eq(param_env, assoc_ty, found) {
@@ -635,7 +647,7 @@ fn foo(&self) -> Self::T { String::new() }
assoc: ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
- msg: &str,
+ msg: impl Fn() -> String,
is_bound_surely_present: bool,
) -> bool {
// FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
@@ -678,7 +690,7 @@ fn foo(&self) -> Self::T { String::new() }
assoc: ty::AssocItem,
assoc_substs: &[ty::GenericArg<'tcx>],
ty: Ty<'tcx>,
- msg: &str,
+ msg: impl Fn() -> String,
) -> bool {
let tcx = self.tcx;
@@ -693,7 +705,7 @@ fn foo(&self) -> Self::T { String::new() }
let item_args = self.format_generic_args(assoc_substs);
(span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
};
- diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
+ diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
return true;
}
false
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index d885d0407..1422bdc9e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span};
use crate::errors::{
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
- FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
- SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
- SuggestTuplePatternOne, TypeErrorAdditionalDiags,
+ FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait,
+ SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne,
+ TypeErrorAdditionalDiags,
};
use super::TypeErrCtxt;
@@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
- /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
- /// suggests it.
- pub(super) fn suggest_as_ref_where_appropriate(
- &self,
- span: Span,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
- && let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
- {
- // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
- let snippet = snippet.trim_start_matches('&');
- let subdiag = match msg {
- SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
- SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
- };
- diag.subdiagnostic(subdiag);
- }
- }
-
pub(super) fn suggest_function_pointers(
&self,
cause: &ObligationCause<'tcx>,
diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs
index 0219167f6..05769b790 100644
--- a/compiler/rustc_infer/src/infer/freshen.rs
+++ b/compiler/rustc_infer/src/infer/freshen.rs
@@ -95,7 +95,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
Entry::Vacant(entry) => {
let index = self.const_freshen_count;
self.const_freshen_count += 1;
- let ct = self.infcx.tcx.mk_const(freshener(index), ty);
+ let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
entry.insert(ct);
ct
}
@@ -188,7 +188,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
match v {
ty::TyVar(v) => {
let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
- Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| self.infcx.tcx.mk_fresh_ty(n)))
+ Some(self.freshen_ty(opt_ty, ty::TyVar(v), |n| Ty::new_fresh(self.infcx.tcx, n)))
}
ty::IntVar(v) => Some(
@@ -200,7 +200,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
.probe_value(v)
.map(|v| v.to_type(self.infcx.tcx)),
ty::IntVar(v),
- |n| self.infcx.tcx.mk_fresh_int_ty(n),
+ |n| Ty::new_fresh_int(self.infcx.tcx, n),
),
),
@@ -213,7 +213,7 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
.probe_value(v)
.map(|v| v.to_type(self.infcx.tcx)),
ty::FloatVar(v),
- |n| self.infcx.tcx.mk_fresh_float_ty(n),
+ |n| Ty::new_fresh_float(self.infcx.tcx, n),
),
),
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index d4a1dacde..780250167 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -277,7 +277,7 @@ where
let origin = *inner.type_variables().var_origin(vid);
let new_var_id =
inner.type_variables().new_var(self.for_universe, origin);
- let u = self.tcx().mk_ty_var(new_var_id);
+ let u = Ty::new_var(self.tcx(), new_var_id);
// Record that we replaced `vid` with `new_var_id` as part of a generalization
// operation. This is needed to detect cyclic types. To see why, see the
@@ -398,7 +398,7 @@ where
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.for_universe },
});
- Ok(self.tcx().mk_const(new_var_id, c.ty()))
+ Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty()))
}
}
}
@@ -412,7 +412,11 @@ where
substs,
substs,
)?;
- Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
+ Ok(ty::Const::new_unevaluated(
+ self.tcx(),
+ ty::UnevaluatedConst { def, substs },
+ c.ty(),
+ ))
}
ty::ConstKind::Placeholder(placeholder) => {
if self.for_universe.can_name(placeholder.universe) {
diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
index c304cd25c..510b1797d 100644
--- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
+++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs
@@ -6,7 +6,7 @@ use super::{HigherRankedType, InferCtxt};
use crate::infer::CombinedSnapshot;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::{self, Binder, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, Binder, Ty, TyCtxt, TypeFoldable};
impl<'a, 'tcx> CombineFields<'a, 'tcx> {
/// Checks whether `for<..> sub <: for<..> sup` holds.
@@ -82,17 +82,20 @@ impl<'tcx> InferCtxt<'tcx> {
let delegate = FnMutDelegate {
regions: &mut |br: ty::BoundRegion| {
- self.tcx
- .mk_re_placeholder(ty::PlaceholderRegion { universe: next_universe, bound: br })
+ ty::Region::new_placeholder(
+ self.tcx,
+ ty::PlaceholderRegion { universe: next_universe, bound: br },
+ )
},
types: &mut |bound_ty: ty::BoundTy| {
- self.tcx.mk_placeholder(ty::PlaceholderType {
- universe: next_universe,
- bound: bound_ty,
- })
+ Ty::new_placeholder(
+ self.tcx,
+ ty::PlaceholderType { universe: next_universe, bound: bound_ty },
+ )
},
consts: &mut |bound_var: ty::BoundVar, ty| {
- self.tcx.mk_const(
+ ty::Const::new_placeholder(
+ self.tcx,
ty::PlaceholderConst { universe: next_universe, bound: bound_var },
ty,
)
@@ -103,13 +106,15 @@ impl<'tcx> InferCtxt<'tcx> {
self.tcx.replace_bound_vars_uncached(binder, delegate)
}
- /// See [RegionConstraintCollector::leak_check][1].
+ /// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
+ /// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
+ /// universe.
///
/// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check
pub fn leak_check(
&self,
- overly_polymorphic: bool,
- snapshot: &CombinedSnapshot<'tcx>,
+ outer_universe: ty::UniverseIndex,
+ only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
) -> RelateResult<'tcx, ()> {
// If the user gave `-Zno-leak-check`, or we have been
// configured to skip the leak check, then skip the leak check
@@ -117,15 +122,15 @@ impl<'tcx> InferCtxt<'tcx> {
// subtyping errors that it would have caught will now be
// caught later on, during region checking. However, we
// continue to use it for a transition period.
- if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() {
+ if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check {
return Ok(());
}
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
self.tcx,
- overly_polymorphic,
+ outer_universe,
self.universe(),
- snapshot,
+ only_consider_snapshot,
)
}
}
diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs
index 7190d33d2..9ef35429f 100644
--- a/compiler/rustc_infer/src/infer/lattice.rs
+++ b/compiler/rustc_infer/src/infer/lattice.rs
@@ -113,7 +113,7 @@ where
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if this.define_opaque_types() == DefineOpaqueTypes::Yes
&& def_id.is_local()
- && !this.tcx().trait_solver_next() =>
+ && !this.infcx().next_trait_solver() =>
{
this.register_obligations(
infcx
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
index 8482ae2aa..485e34fe2 100644
--- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -347,7 +347,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// name the placeholder, then the placeholder is
// larger; otherwise, the only ancestor is `'static`.
Err(placeholder) if empty_ui.can_name(placeholder.universe) => {
- self.tcx().mk_re_placeholder(placeholder)
+ ty::Region::new_placeholder(self.tcx(), placeholder)
}
Err(_) => self.tcx().lifetimes.re_static,
};
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index cd99fc312..fca32b73d 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -6,6 +6,7 @@ pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*;
pub use combine::ObligationEmittingRelation;
+use rustc_data_structures::undo_log::UndoLogs;
use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
@@ -251,14 +252,13 @@ pub struct InferCtxt<'tcx> {
/// solving is left to borrowck instead.
pub considering_regions: bool,
- pub inner: RefCell<InferCtxtInner<'tcx>>,
-
/// If set, this flag causes us to skip the 'leak check' during
/// higher-ranked subtyping operations. This flag is a temporary one used
/// to manage the removal of the leak-check: for the time being, we still run the
- /// leak-check, but we issue warnings. This flag can only be set to true
- /// when entering a snapshot.
- skip_leak_check: Cell<bool>,
+ /// leak-check, but we issue warnings.
+ skip_leak_check: bool,
+
+ pub inner: RefCell<InferCtxtInner<'tcx>>,
/// Once region inference is done, the values for each variable.
lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
@@ -298,9 +298,6 @@ pub struct InferCtxt<'tcx> {
// FIXME(matthewjasper) Merge into `tainted_by_errors`
err_count_on_creation: usize,
- /// This flag is true while there is an active snapshot.
- in_snapshot: Cell<bool>,
-
/// What is the innermost universe we have created? Starts out as
/// `UniverseIndex::root()` but grows from there as we enter
/// universal quantifiers.
@@ -331,6 +328,8 @@ pub struct InferCtxt<'tcx> {
/// there is no type that the user could *actually name* that
/// would satisfy it. This avoids crippling inference, basically.
pub intercrate: bool,
+
+ next_trait_solver: bool,
}
/// See the `error_reporting` module for more details.
@@ -543,8 +542,12 @@ pub struct InferCtxtBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
defining_use_anchor: DefiningAnchor,
considering_regions: bool,
+ skip_leak_check: bool,
/// Whether we are in coherence mode.
intercrate: bool,
+ /// Whether we should use the new trait solver in the local inference context,
+ /// which affects things like which solver is used in `predicate_may_hold`.
+ next_trait_solver: bool,
}
pub trait TyCtxtInferExt<'tcx> {
@@ -557,7 +560,9 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
tcx: self,
defining_use_anchor: DefiningAnchor::Error,
considering_regions: true,
+ skip_leak_check: false,
intercrate: false,
+ next_trait_solver: self.next_trait_solver_globally(),
}
}
}
@@ -574,6 +579,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
self
}
+ pub fn with_next_trait_solver(mut self, next_trait_solver: bool) -> Self {
+ self.next_trait_solver = next_trait_solver;
+ self
+ }
+
pub fn intercrate(mut self, intercrate: bool) -> Self {
self.intercrate = intercrate;
self
@@ -584,6 +594,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
self
}
+ pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
+ self.skip_leak_check = skip_leak_check;
+ self
+ }
+
/// Given a canonical value `C` as a starting point, create an
/// inference context that contains each of the bound values
/// within instantiated as a fresh variable. The `f` closure is
@@ -605,11 +620,19 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
}
pub fn build(&mut self) -> InferCtxt<'tcx> {
- let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self;
+ let InferCtxtBuilder {
+ tcx,
+ defining_use_anchor,
+ considering_regions,
+ skip_leak_check,
+ intercrate,
+ next_trait_solver,
+ } = *self;
InferCtxt {
tcx,
defining_use_anchor,
considering_regions,
+ skip_leak_check,
inner: RefCell::new(InferCtxtInner::new()),
lexical_region_resolutions: RefCell::new(None),
selection_cache: Default::default(),
@@ -618,10 +641,9 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
reported_closure_mismatch: Default::default(),
tainted_by_errors: Cell::new(None),
err_count_on_creation: tcx.sess.err_count(),
- in_snapshot: Cell::new(false),
- skip_leak_check: Cell::new(false),
universe: Cell::new(ty::UniverseIndex::ROOT),
intercrate,
+ next_trait_solver,
}
}
}
@@ -654,10 +676,13 @@ pub struct CombinedSnapshot<'tcx> {
undo_snapshot: Snapshot<'tcx>,
region_constraints_snapshot: RegionSnapshot,
universe: ty::UniverseIndex,
- was_in_snapshot: bool,
}
impl<'tcx> InferCtxt<'tcx> {
+ pub fn next_trait_solver(&self) -> bool {
+ self.next_trait_solver
+ }
+
/// Creates a `TypeErrCtxt` for emitting various inference errors.
/// During typeck, use `FnCtxt::err_ctxt` instead.
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
@@ -673,10 +698,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
- pub fn is_in_snapshot(&self) -> bool {
- self.in_snapshot.get()
- }
-
pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
t.fold_with(&mut self.freshener())
}
@@ -704,19 +725,19 @@ impl<'tcx> InferCtxt<'tcx> {
.type_variables()
.unsolved_variables()
.into_iter()
- .map(|t| self.tcx.mk_ty_var(t))
+ .map(|t| Ty::new_var(self.tcx, t))
.collect();
vars.extend(
(0..inner.int_unification_table().len())
.map(|i| ty::IntVid { index: i as u32 })
.filter(|&vid| inner.int_unification_table().probe_value(vid).is_none())
- .map(|v| self.tcx.mk_int_var(v)),
+ .map(|v| Ty::new_int_var(self.tcx, v)),
);
vars.extend(
(0..inner.float_unification_table().len())
.map(|i| ty::FloatVid { index: i as u32 })
.filter(|&vid| inner.float_unification_table().probe_value(vid).is_none())
- .map(|v| self.tcx.mk_float_var(v)),
+ .map(|v| Ty::new_float_var(self.tcx, v)),
);
vars
}
@@ -737,31 +758,30 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
+ pub fn in_snapshot(&self) -> bool {
+ UndoLogs::<UndoLog<'tcx>>::in_snapshot(&self.inner.borrow_mut().undo_log)
+ }
+
+ pub fn num_open_snapshots(&self) -> usize {
+ UndoLogs::<UndoLog<'tcx>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
+ }
+
fn start_snapshot(&self) -> CombinedSnapshot<'tcx> {
debug!("start_snapshot()");
- let in_snapshot = self.in_snapshot.replace(true);
-
let mut inner = self.inner.borrow_mut();
CombinedSnapshot {
undo_snapshot: inner.undo_log.start_snapshot(),
region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(),
universe: self.universe(),
- was_in_snapshot: in_snapshot,
}
}
#[instrument(skip(self, snapshot), level = "debug")]
fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'tcx>) {
- let CombinedSnapshot {
- undo_snapshot,
- region_constraints_snapshot,
- universe,
- was_in_snapshot,
- } = snapshot;
-
- self.in_snapshot.set(was_in_snapshot);
+ let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
+
self.universe.set(universe);
let mut inner = self.inner.borrow_mut();
@@ -771,14 +791,8 @@ impl<'tcx> InferCtxt<'tcx> {
#[instrument(skip(self, snapshot), level = "debug")]
fn commit_from(&self, snapshot: CombinedSnapshot<'tcx>) {
- let CombinedSnapshot {
- undo_snapshot,
- region_constraints_snapshot: _,
- universe: _,
- was_in_snapshot,
- } = snapshot;
-
- self.in_snapshot.set(was_in_snapshot);
+ let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } =
+ snapshot;
self.inner.borrow_mut().commit(undo_snapshot);
}
@@ -815,32 +829,9 @@ impl<'tcx> InferCtxt<'tcx> {
r
}
- /// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
- #[instrument(skip(self, f), level = "debug")]
- pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
- where
- F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
- {
- let snapshot = self.start_snapshot();
- let was_skip_leak_check = self.skip_leak_check.get();
- if should_skip {
- self.skip_leak_check.set(true);
- }
- let r = f(&snapshot);
- self.rollback_to("probe", snapshot);
- self.skip_leak_check.set(was_skip_leak_check);
- r
- }
-
- /// Scan the constraints produced since `snapshot` began and returns:
- ///
- /// - `None` -- if none of them involves "region outlives" constraints.
- /// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder.
- /// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders.
- pub fn region_constraints_added_in_snapshot(
- &self,
- snapshot: &CombinedSnapshot<'tcx>,
- ) -> Option<bool> {
+ /// Scan the constraints produced since `snapshot` and check whether
+ /// we added any region constraints.
+ pub fn region_constraints_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'tcx>) -> bool {
self.inner
.borrow_mut()
.unwrap_region_constraints()
@@ -987,7 +978,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
- self.tcx.mk_ty_var(self.next_ty_var_id(origin))
+ Ty::new_var(self.tcx, self.next_ty_var_id(origin))
}
pub fn next_ty_var_id_in_universe(
@@ -1004,11 +995,11 @@ impl<'tcx> InferCtxt<'tcx> {
universe: ty::UniverseIndex,
) -> Ty<'tcx> {
let vid = self.next_ty_var_id_in_universe(origin, universe);
- self.tcx.mk_ty_var(vid)
+ Ty::new_var(self.tcx, vid)
}
pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> {
- self.tcx.mk_const(self.next_const_var_id(origin), ty)
+ ty::Const::new_var(self.tcx, self.next_const_var_id(origin), ty)
}
pub fn next_const_var_in_universe(
@@ -1022,7 +1013,7 @@ impl<'tcx> InferCtxt<'tcx> {
.borrow_mut()
.const_unification_table()
.new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } });
- self.tcx.mk_const(vid, ty)
+ ty::Const::new_var(self.tcx, vid, ty)
}
pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> {
@@ -1037,7 +1028,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
pub fn next_int_var(&self) -> Ty<'tcx> {
- self.tcx.mk_int_var(self.next_int_var_id())
+ Ty::new_int_var(self.tcx, self.next_int_var_id())
}
fn next_float_var_id(&self) -> FloatVid {
@@ -1045,7 +1036,7 @@ impl<'tcx> InferCtxt<'tcx> {
}
pub fn next_float_var(&self) -> Ty<'tcx> {
- self.tcx.mk_float_var(self.next_float_var_id())
+ Ty::new_float_var(self.tcx, self.next_float_var_id())
}
/// Creates a fresh region variable with the next available index.
@@ -1065,7 +1056,7 @@ impl<'tcx> InferCtxt<'tcx> {
) -> ty::Region<'tcx> {
let region_var =
self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, origin);
- self.tcx.mk_re_var(region_var)
+ ty::Region::new_var(self.tcx, region_var)
}
/// Return the universe that the region `r` was created in. For
@@ -1119,13 +1110,13 @@ impl<'tcx> InferCtxt<'tcx> {
TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeParameterDefinition(
param.name,
- Some(param.def_id),
+ param.def_id,
),
span,
},
);
- self.tcx.mk_ty_var(ty_var_id).into()
+ Ty::new_var(self.tcx, ty_var_id).into()
}
GenericParamDefKind::Const { .. } => {
let origin = ConstVariableOrigin {
@@ -1140,15 +1131,15 @@ impl<'tcx> InferCtxt<'tcx> {
origin,
val: ConstVariableValue::Unknown { universe: self.universe() },
});
- self.tcx
- .mk_const(
- const_var_id,
- self.tcx
- .type_of(param.def_id)
- .no_bound_vars()
- .expect("const parameter types cannot be generic"),
- )
- .into()
+ ty::Const::new_var(
+ self.tcx,
+ const_var_id,
+ self.tcx
+ .type_of(param.def_id)
+ .no_bound_vars()
+ .expect("const parameter types cannot be generic"),
+ )
+ .into()
}
}
}
@@ -1206,15 +1197,15 @@ impl<'tcx> InferCtxt<'tcx> {
.var_origin(vid)
}
- /// Takes ownership of the list of variable regions. This implies
- /// that all the region constraints have already been taken, and
- /// hence that `resolve_regions_and_report_errors` can never be
- /// called. This is used only during NLL processing to "hand off" ownership
- /// of the set of region variables into the NLL region context.
+ /// Clone the list of variable regions. This is used only during NLL processing
+ /// to put the set of region variables into the NLL region context.
pub fn get_region_var_origins(&self) -> VarInfos {
let mut inner = self.inner.borrow_mut();
let (var_infos, data) = inner
.region_constraint_storage
+ // We clone instead of taking because borrowck still wants to use
+ // the inference context after calling this for diagnostics
+ // and the new trait solver.
.clone()
.expect("regions already resolved")
.with_log(&mut inner.undo_log)
@@ -1274,7 +1265,7 @@ impl<'tcx> InferCtxt<'tcx> {
if let Some(value) = inner.int_unification_table().probe_value(vid) {
value.to_type(self.tcx)
} else {
- self.tcx.mk_int_var(inner.int_unification_table().find(vid))
+ Ty::new_int_var(self.tcx, inner.int_unification_table().find(vid))
}
}
@@ -1285,7 +1276,7 @@ impl<'tcx> InferCtxt<'tcx> {
if let Some(value) = inner.float_unification_table().probe_value(vid) {
value.to_type(self.tcx)
} else {
- self.tcx.mk_float_var(inner.float_unification_table().find(vid))
+ Ty::new_float_var(self.tcx, inner.float_unification_table().find(vid))
}
}
@@ -1468,6 +1459,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// universes. Updates `self.universe` to that new universe.
pub fn create_next_universe(&self) -> ty::UniverseIndex {
let u = self.universe.get().next_universe();
+ debug!("create_next_universe {u:?}");
self.universe.set(u);
u
}
@@ -1480,7 +1472,7 @@ impl<'tcx> InferCtxt<'tcx> {
span: Option<Span>,
) -> Result<ty::Const<'tcx>, ErrorHandled> {
match self.const_eval_resolve(param_env, unevaluated, span) {
- Ok(Some(val)) => Ok(self.tcx.mk_const(val, ty)),
+ Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)),
Ok(None) => {
let tcx = self.tcx;
let def_id = unevaluated.def;
@@ -1953,13 +1945,16 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>(
self.idx += 1;
idx
};
- self.tcx.mk_placeholder(ty::PlaceholderType {
- universe: ty::UniverseIndex::ROOT,
- bound: ty::BoundTy {
- var: ty::BoundVar::from_u32(idx),
- kind: ty::BoundTyKind::Anon,
+ Ty::new_placeholder(
+ self.tcx,
+ ty::PlaceholderType {
+ universe: ty::UniverseIndex::ROOT,
+ bound: ty::BoundTy {
+ var: ty::BoundVar::from_u32(idx),
+ kind: ty::BoundTyKind::Anon,
+ },
},
- })
+ )
} else {
t.super_fold_with(self)
}
@@ -1972,7 +1967,8 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>(
if ty.has_non_region_param() || ty.has_non_region_infer() {
bug!("const `{c}`'s type should not reference params or types");
}
- self.tcx.mk_const(
+ ty::Const::new_placeholder(
+ self.tcx,
ty::PlaceholderConst {
universe: ty::UniverseIndex::ROOT,
bound: ty::BoundVar::from_u32({
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index d3fd01b96..71c07f31b 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -491,12 +491,12 @@ where
(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
- ) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => {
+ ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
infcx.super_combine_tys(self, a, b).or_else(|err| {
// This behavior is only there for the old solver, the new solver
// shouldn't ever fail. Instead, it unconditionally emits an
// alias-relate goal.
- assert!(!self.tcx().trait_solver_next());
+ assert!(!self.infcx.next_trait_solver());
self.tcx().sess.delay_span_bug(
self.delegate.span(),
"failure to relate an opaque to itself should result in an error later on",
@@ -506,7 +506,7 @@ where
}
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
- if def_id.is_local() && !self.tcx().trait_solver_next() =>
+ if def_id.is_local() && !self.infcx.next_trait_solver() =>
{
self.relate_opaques(a, b)
}
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 9d5ec228d..5927f79a1 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -33,9 +33,6 @@ pub struct OpaqueTypeDecl<'tcx> {
/// There can be multiple, but they are all `lub`ed together at the end
/// to obtain the canonical hidden type.
pub hidden_type: OpaqueHiddenType<'tcx>,
-
- /// The origin of the opaque type.
- pub origin: hir::OpaqueTyOrigin,
}
impl<'tcx> InferCtxt<'tcx> {
@@ -49,7 +46,7 @@ impl<'tcx> InferCtxt<'tcx> {
param_env: ty::ParamEnv<'tcx>,
) -> InferOk<'tcx, T> {
// We handle opaque types differently in the new solver.
- if self.tcx.trait_solver_next() {
+ if self.next_trait_solver() {
return InferOk { value, obligations: vec![] };
}
@@ -108,7 +105,7 @@ impl<'tcx> InferCtxt<'tcx> {
let process = |a: Ty<'tcx>, b: Ty<'tcx>, a_is_expected| match *a.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) if def_id.is_local() => {
let def_id = def_id.expect_local();
- let origin = match self.defining_use_anchor {
+ match self.defining_use_anchor {
DefiningAnchor::Bind(_) => {
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
@@ -144,9 +141,11 @@ impl<'tcx> InferCtxt<'tcx> {
// let x = || foo(); // returns the Opaque assoc with `foo`
// }
// ```
- self.opaque_type_origin(def_id)?
+ if self.opaque_type_origin(def_id).is_none() {
+ return None;
+ }
}
- DefiningAnchor::Bubble => self.opaque_type_origin_unchecked(def_id),
+ DefiningAnchor::Bubble => {}
DefiningAnchor::Error => return None,
};
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
@@ -170,7 +169,6 @@ impl<'tcx> InferCtxt<'tcx> {
cause.clone(),
param_env,
b,
- origin,
a_is_expected,
))
}
@@ -380,7 +378,7 @@ impl<'tcx> InferCtxt<'tcx> {
DefiningAnchor::Bind(bind) => bind,
};
- let origin = self.opaque_type_origin_unchecked(def_id);
+ let origin = self.tcx.opaque_type_origin(def_id);
let in_definition_scope = match origin {
// Async `impl Trait`
hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id,
@@ -397,13 +395,6 @@ impl<'tcx> InferCtxt<'tcx> {
};
in_definition_scope.then_some(origin)
}
-
- /// Returns the origin of the opaque type `def_id` even if we are not in its
- /// defining scope.
- #[instrument(skip(self), level = "trace", ret)]
- fn opaque_type_origin_unchecked(&self, def_id: LocalDefId) -> OpaqueTyOrigin {
- self.tcx.hir().expect_item(def_id).expect_opaque_ty().origin
- }
}
/// Visitor that requires that (almost) all regions in the type visited outlive
@@ -482,17 +473,6 @@ where
}
}
- ty::Alias(ty::Projection, proj) if self.tcx.is_impl_trait_in_trait(proj.def_id) => {
- // Skip lifetime parameters that are not captures.
- let variances = self.tcx.variances_of(proj.def_id);
-
- for (v, s) in std::iter::zip(variances, proj.substs.iter()) {
- if *v != ty::Variance::Bivariant {
- s.visit_with(self);
- }
- }
- }
-
_ => {
ty.super_visit_with(self);
}
@@ -524,30 +504,22 @@ impl<'tcx> InferCtxt<'tcx> {
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
- origin: hir::OpaqueTyOrigin,
a_is_expected: bool,
) -> InferResult<'tcx, ()> {
- // Ideally, we'd get the span where *this specific `ty` came
- // from*, but right now we just use the span from the overall
- // value being folded. In simple cases like `-> impl Foo`,
- // these are the same span, but not in cases like `-> (impl
- // Foo, impl Bar)`.
- let span = cause.span;
- let prev = self.inner.borrow_mut().opaque_types().register(
+ let mut obligations = Vec::new();
+
+ self.insert_hidden_type(
opaque_type_key,
- OpaqueHiddenType { ty: hidden_ty, span },
- origin,
- );
- let mut obligations = if let Some(prev) = prev {
- self.at(&cause, param_env)
- .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)?
- .obligations
- } else {
- Vec::new()
- };
+ &cause,
+ param_env,
+ hidden_ty,
+ a_is_expected,
+ &mut obligations,
+ )?;
self.add_item_bounds_for_hidden_type(
- opaque_type_key,
+ opaque_type_key.def_id.to_def_id(),
+ opaque_type_key.substs,
cause,
param_env,
hidden_ty,
@@ -557,32 +529,60 @@ impl<'tcx> InferCtxt<'tcx> {
Ok(InferOk { value: (), obligations })
}
- /// Registers an opaque's hidden type -- only should be used when the opaque
- /// can be defined. For something more fallible -- checks the anchors, tries
- /// to unify opaques in both dirs, etc. -- use `InferCtxt::handle_opaque_type`.
- pub fn register_hidden_type_in_new_solver(
+ /// Insert a hidden type into the opaque type storage, equating it
+ /// with any previous entries if necessary.
+ ///
+ /// This **does not** add the item bounds of the opaque as nested
+ /// obligations. That is only necessary when normalizing the opaque
+ /// itself, not when getting the opaque type constraints from
+ /// somewhere else.
+ pub fn insert_hidden_type(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
+ cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
- ) -> InferResult<'tcx, ()> {
- assert!(self.tcx.trait_solver_next());
- let origin = self
- .opaque_type_origin(opaque_type_key.def_id)
- .expect("should be called for defining usages only");
- self.register_hidden_type(
- opaque_type_key,
- ObligationCause::dummy(),
- param_env,
- hidden_ty,
- origin,
- true,
- )
+ a_is_expected: bool,
+ obligations: &mut Vec<PredicateObligation<'tcx>>,
+ ) -> Result<(), TypeError<'tcx>> {
+ // Ideally, we'd get the span where *this specific `ty` came
+ // from*, but right now we just use the span from the overall
+ // value being folded. In simple cases like `-> impl Foo`,
+ // these are the same span, but not in cases like `-> (impl
+ // Foo, impl Bar)`.
+ let span = cause.span;
+ if self.intercrate {
+ // During intercrate we do not define opaque types but instead always
+ // force ambiguity unless the hidden type is known to not implement
+ // our trait.
+ obligations.push(traits::Obligation::new(
+ self.tcx,
+ cause.clone(),
+ param_env,
+ ty::PredicateKind::Ambiguous,
+ ))
+ } else {
+ let prev = self
+ .inner
+ .borrow_mut()
+ .opaque_types()
+ .register(opaque_type_key, OpaqueHiddenType { ty: hidden_ty, span });
+ if let Some(prev) = prev {
+ obligations.extend(
+ self.at(&cause, param_env)
+ .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)?
+ .obligations,
+ );
+ }
+ };
+
+ Ok(())
}
pub fn add_item_bounds_for_hidden_type(
&self,
- OpaqueTypeKey { def_id, substs }: OpaqueTypeKey<'tcx>,
+ def_id: DefId,
+ substs: ty::SubstsRef<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
@@ -602,7 +602,7 @@ impl<'tcx> InferCtxt<'tcx> {
ty::Alias(ty::Projection, projection_ty)
if !projection_ty.has_escaping_bound_vars()
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id)
- && !tcx.trait_solver_next() =>
+ && !self.next_trait_solver() =>
{
self.infer_projection(
param_env,
@@ -615,7 +615,7 @@ impl<'tcx> InferCtxt<'tcx> {
// Replace all other mentions of the same opaque type with the hidden type,
// as the bounds must hold on the hidden type after all.
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def_id2, substs: substs2, .. })
- if def_id.to_def_id() == def_id2 && substs == substs2 =>
+ if def_id == def_id2 && substs == substs2 =>
{
hidden_ty
}
@@ -624,16 +624,14 @@ impl<'tcx> InferCtxt<'tcx> {
ty::Alias(
ty::Projection,
ty::AliasTy { def_id: def_id2, substs: substs2, .. },
- ) if def_id.to_def_id() == def_id2 && substs == substs2 => hidden_ty,
+ ) if def_id == def_id2 && substs == substs2 => hidden_ty,
_ => ty,
},
lt_op: |lt| lt,
ct_op: |ct| ct,
});
- if let ty::PredicateKind::Clause(ty::Clause::Projection(projection)) =
- predicate.kind().skip_binder()
- {
+ if let ty::ClauseKind::Projection(projection) = predicate.kind().skip_binder() {
if projection.term.references_error() {
// No point on adding any obligations since there's a type error involved.
obligations.clear();
diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs
index a0f6d7eca..a737761ba 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/table.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs
@@ -1,5 +1,4 @@
use rustc_data_structures::undo_log::UndoLogs;
-use rustc_hir::OpaqueTyOrigin;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty};
use rustc_span::DUMMY_SP;
@@ -60,14 +59,13 @@ impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> {
&mut self,
key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
- origin: OpaqueTyOrigin,
) -> Option<Ty<'tcx>> {
if let Some(decl) = self.storage.opaque_types.get_mut(&key) {
let prev = std::mem::replace(&mut decl.hidden_type, hidden_type);
self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev)));
return Some(prev.ty);
}
- let decl = OpaqueTypeDecl { hidden_type, origin };
+ let decl = OpaqueTypeDecl { hidden_type };
self.storage.opaque_types.insert(key, decl);
self.undo_log.push(UndoLog::OpaqueTypes(key, None));
None
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 8a44d5031..cb92fc6dd 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -20,27 +20,18 @@ pub fn explicit_outlives_bounds<'tcx>(
param_env
.caller_bounds()
.into_iter()
- .map(ty::Predicate::kind)
+ .map(ty::Clause::kind)
.filter_map(ty::Binder::no_bound_vars)
.filter_map(move |kind| match kind {
- ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
- r_a,
- r_b,
- ))) => Some(OutlivesBound::RegionSubRegion(r_b, r_a)),
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
+ Some(OutlivesBound::RegionSubRegion(r_b, r_a))
+ }
+ ty::ClauseKind::Trait(_)
+ | ty::ClauseKind::TypeOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => None,
})
}
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 9c20c814b..73df6d03f 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -125,10 +125,7 @@ impl<'tcx> InferCtxt<'tcx> {
/// right before lexical region resolution.
#[instrument(level = "debug", skip(self, outlives_env))]
pub fn process_registered_region_obligations(&self, outlives_env: &OutlivesEnvironment<'tcx>) {
- assert!(
- !self.in_snapshot.get(),
- "cannot process registered region obligations in a snapshot"
- );
+ assert!(!self.in_snapshot(), "cannot process registered region obligations in a snapshot");
let my_region_obligations = self.take_registered_region_obligations();
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index c2bf0f3db..1a5e2b520 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -223,7 +223,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
// parameter environments are already elaborated, so we don't
// have to worry about that.
let c_b = self.param_env.caller_bounds();
- let param_bounds = self.collect_outlives_from_predicate_list(erased_ty, c_b.into_iter());
+ let param_bounds = self.collect_outlives_from_clause_list(erased_ty, c_b.into_iter());
// Next, collect regions we scraped from the well-formedness
// constraints in the fn signature. To do that, we walk the list
@@ -293,10 +293,10 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
let bounds = tcx.item_bounds(alias_ty.def_id);
- trace!("{:#?}", bounds.0);
+ trace!("{:#?}", bounds.skip_binder());
bounds
.subst_iter(tcx, alias_ty.substs)
- .filter_map(|p| p.to_opt_type_outlives())
+ .filter_map(|p| p.as_type_outlives_clause())
.filter_map(|p| p.no_bound_vars())
.map(|OutlivesPredicate(_, r)| r)
}
@@ -307,15 +307,15 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
/// when comparing `ty` for equality, so `ty` must be something
/// that does not involve inference variables and where you
/// otherwise want a precise match.
- fn collect_outlives_from_predicate_list(
+ fn collect_outlives_from_clause_list(
&self,
erased_ty: Ty<'tcx>,
- predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+ clauses: impl Iterator<Item = ty::Clause<'tcx>>,
) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
{
let tcx = self.tcx;
let param_env = self.param_env;
- predicates.filter_map(|p| p.to_opt_type_outlives()).filter(move |outlives_predicate| {
+ clauses.filter_map(|p| p.as_type_outlives_clause()).filter(move |outlives_predicate| {
super::test_type_match::can_match_erased_ty(
tcx,
param_env,
diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs
index fa6529dfa..38e74e538 100644
--- a/compiler/rustc_infer/src/infer/projection.rs
+++ b/compiler/rustc_infer/src/infer/projection.rs
@@ -21,28 +21,18 @@ impl<'tcx> InferCtxt<'tcx> {
recursion_depth: usize,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Ty<'tcx> {
- if self.tcx.trait_solver_next() {
- // FIXME(-Ztrait-solver=next): Instead of branching here,
- // completely change the normalization routine with the new solver.
- //
- // The new solver correctly handles projection equality so this hack
- // is not necessary. if re-enabled it should emit `PredicateKind::AliasRelate`
- // not `PredicateKind::Clause(Clause::Projection(..))` as in the new solver
- // `Projection` is used as `normalizes-to` which will fail for `<T as Trait>::Assoc eq ?0`.
- return projection_ty.to_ty(self.tcx);
- } else {
- let def_id = projection_ty.def_id;
- let ty_var = self.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::NormalizeProjectionType,
- span: self.tcx.def_span(def_id),
- });
- let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
- ty::ProjectionPredicate { projection_ty, term: ty_var.into() },
- )));
- let obligation =
- Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
- obligations.push(obligation);
- ty_var
- }
+ debug_assert!(!self.next_trait_solver());
+ let def_id = projection_ty.def_id;
+ let ty_var = self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::NormalizeProjectionType,
+ span: self.tcx.def_span(def_id),
+ });
+ let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::Projection(
+ ty::ProjectionPredicate { projection_ty, term: ty_var.into() },
+ )));
+ let obligation =
+ Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
+ obligations.push(obligation);
+ ty_var
}
}
diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
index 89cfc9ea3..dd65f66cc 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs
@@ -3,7 +3,6 @@ use crate::infer::CombinedSnapshot;
use rustc_data_structures::{
fx::FxIndexMap,
graph::{scc::Sccs, vec_graph::VecGraph},
- undo_log::UndoLogs,
};
use rustc_index::Idx;
use rustc_middle::ty::error::TypeError;
@@ -13,7 +12,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
/// Searches new universes created during `snapshot`, looking for
/// placeholders that may "leak" out from the universes they are contained
/// in. If any leaking placeholders are found, then an `Err` is returned
- /// (typically leading to the snapshot being reversed).
+ /// (typically leading to the snapshot being reversed). This algorithm
+ /// only looks at placeholders which cannot be named by `outer_universe`,
+ /// as this is the universe we're currently checking for a leak.
///
/// The leak check *used* to be the only way we had to handle higher-ranked
/// obligations. Now that we have integrated universes into the region
@@ -55,6 +56,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
/// indicates `P: R` and `R` is in an incompatible universe
///
+ /// To improve performance and for the old trait solver caching to be sound, this takes
+ /// an optional snapshot in which case we only look at region constraints added in that
+ /// snapshot. If we were to not do that the `leak_check` during evaluation can rely on
+ /// region constraints added outside of that evaluation. As that is not reflected in the
+ /// cache key this would be unsound.
+ ///
/// # Historical note
///
/// Older variants of the leak check used to report errors for these
@@ -62,36 +69,21 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
///
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
/// * R: P1, R: P2, as above
+ #[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
pub fn leak_check(
&mut self,
tcx: TyCtxt<'tcx>,
- overly_polymorphic: bool,
+ outer_universe: ty::UniverseIndex,
max_universe: ty::UniverseIndex,
- snapshot: &CombinedSnapshot<'tcx>,
+ only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
) -> RelateResult<'tcx, ()> {
- debug!(
- "leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})",
- max_universe, snapshot.universe, overly_polymorphic
- );
-
- assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
-
- let universe_at_start_of_snapshot = snapshot.universe;
- if universe_at_start_of_snapshot == max_universe {
+ if outer_universe == max_universe {
return Ok(());
}
- let mini_graph =
- &MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
+ let mini_graph = &MiniGraph::new(tcx, &self, only_consider_snapshot);
- let mut leak_check = LeakCheck::new(
- tcx,
- universe_at_start_of_snapshot,
- max_universe,
- overly_polymorphic,
- mini_graph,
- self,
- );
+ let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
leak_check.assign_placeholder_values()?;
leak_check.propagate_scc_value()?;
Ok(())
@@ -100,9 +92,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
struct LeakCheck<'me, 'tcx> {
tcx: TyCtxt<'tcx>,
- universe_at_start_of_snapshot: ty::UniverseIndex,
- /// Only used when reporting region errors.
- overly_polymorphic: bool,
+ outer_universe: ty::UniverseIndex,
mini_graph: &'me MiniGraph<'tcx>,
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
@@ -130,17 +120,15 @@ struct LeakCheck<'me, 'tcx> {
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
fn new(
tcx: TyCtxt<'tcx>,
- universe_at_start_of_snapshot: ty::UniverseIndex,
+ outer_universe: ty::UniverseIndex,
max_universe: ty::UniverseIndex,
- overly_polymorphic: bool,
mini_graph: &'me MiniGraph<'tcx>,
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
) -> Self {
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
Self {
tcx,
- universe_at_start_of_snapshot,
- overly_polymorphic,
+ outer_universe,
mini_graph,
rcc,
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
@@ -165,7 +153,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
// Detect those SCCs that directly contain a placeholder
if let ty::RePlaceholder(placeholder) = **region {
- if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
+ if self.outer_universe.cannot_name(placeholder.universe) {
self.assign_scc_value(scc, placeholder)?;
}
}
@@ -280,7 +268,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
placeholder1: ty::PlaceholderRegion,
placeholder2: ty::PlaceholderRegion,
) -> TypeError<'tcx> {
- self.error(placeholder1, self.tcx.mk_re_placeholder(placeholder2))
+ self.error(placeholder1, ty::Region::new_placeholder(self.tcx, placeholder2))
}
fn error(
@@ -289,11 +277,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
other_region: ty::Region<'tcx>,
) -> TypeError<'tcx> {
debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
- if self.overly_polymorphic {
- TypeError::RegionsOverlyPolymorphic(placeholder.bound.kind, other_region)
- } else {
- TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
- }
+ TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
}
}
@@ -379,56 +363,70 @@ struct MiniGraph<'tcx> {
}
impl<'tcx> MiniGraph<'tcx> {
- fn new<'a>(
+ fn new(
tcx: TyCtxt<'tcx>,
- undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
- verifys: &[Verify<'tcx>],
- ) -> Self
- where
- 'tcx: 'a,
- {
+ region_constraints: &RegionConstraintCollector<'_, 'tcx>,
+ only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
+ ) -> Self {
let mut nodes = FxIndexMap::default();
let mut edges = Vec::new();
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
- Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
- let source_node = Self::add_node(&mut nodes, source);
- let target_node = Self::add_node(&mut nodes, target);
- edges.push((source_node, target_node));
- });
+ Self::iterate_region_constraints(
+ tcx,
+ region_constraints,
+ only_consider_snapshot,
+ |target, source| {
+ let source_node = Self::add_node(&mut nodes, source);
+ let target_node = Self::add_node(&mut nodes, target);
+ edges.push((source_node, target_node));
+ },
+ );
let graph = VecGraph::new(nodes.len(), edges);
let sccs = Sccs::new(&graph);
Self { nodes, sccs }
}
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
- fn iterate_undo_log<'a>(
+ fn iterate_region_constraints(
tcx: TyCtxt<'tcx>,
- undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
- verifys: &[Verify<'tcx>],
+ region_constraints: &RegionConstraintCollector<'_, 'tcx>,
+ only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
- ) where
- 'tcx: 'a,
- {
- for undo_entry in undo_log {
- match undo_entry {
- &AddConstraint(Constraint::VarSubVar(a, b)) => {
- each_edge(tcx.mk_re_var(a), tcx.mk_re_var(b));
- }
- &AddConstraint(Constraint::RegSubVar(a, b)) => {
- each_edge(a, tcx.mk_re_var(b));
- }
- &AddConstraint(Constraint::VarSubReg(a, b)) => {
- each_edge(tcx.mk_re_var(a), b);
- }
- &AddConstraint(Constraint::RegSubReg(a, b)) => {
- each_edge(a, b);
+ ) {
+ let mut each_constraint = |constraint| match constraint {
+ &Constraint::VarSubVar(a, b) => {
+ each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
+ }
+ &Constraint::RegSubVar(a, b) => {
+ each_edge(a, ty::Region::new_var(tcx, b));
+ }
+ &Constraint::VarSubReg(a, b) => {
+ each_edge(ty::Region::new_var(tcx, a), b);
+ }
+ &Constraint::RegSubReg(a, b) => {
+ each_edge(a, b);
+ }
+ };
+
+ if let Some(snapshot) = only_consider_snapshot {
+ for undo_entry in
+ region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot)
+ {
+ match undo_entry {
+ AddConstraint(constraint) => {
+ each_constraint(constraint);
+ }
+ &AddVerify(i) => span_bug!(
+ region_constraints.data().verifys[i].origin.span(),
+ "we never add verifications while doing higher-ranked things",
+ ),
+ &AddCombination(..) | &AddVar(..) => {}
}
- &AddVerify(i) => span_bug!(
- verifys[i].origin.span(),
- "we never add verifications while doing higher-ranked things",
- ),
- &AddCombination(..) | &AddVar(..) => {}
+ }
+ } else {
+ for (constraint, _origin) in &region_constraints.data().constraints {
+ each_constraint(constraint)
}
}
}
diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
index c7a307b89..613da8a0b 100644
--- a/compiler/rustc_infer/src/infer/region_constraints/mod.rs
+++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs
@@ -400,7 +400,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
data
}
- pub(super) fn data(&self) -> &RegionConstraintData<'tcx> {
+ pub fn data(&self) -> &RegionConstraintData<'tcx> {
&self.data
}
@@ -610,13 +610,13 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
let resolved = ut
.probe_value(root_vid)
.get_value_ignoring_universes()
- .unwrap_or_else(|| tcx.mk_re_var(root_vid));
+ .unwrap_or_else(|| ty::Region::new_var(tcx, root_vid));
// Don't resolve a variable to a region that it cannot name.
if self.var_universe(vid).can_name(self.universe(resolved)) {
resolved
} else {
- tcx.mk_re_var(vid)
+ ty::Region::new_var(tcx, vid)
}
}
@@ -637,7 +637,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
) -> Region<'tcx> {
let vars = TwoRegions { a, b };
if let Some(&c) = self.combine_map(t).get(&vars) {
- return tcx.mk_re_var(c);
+ return ty::Region::new_var(tcx, c);
}
let a_universe = self.universe(a);
let b_universe = self.universe(b);
@@ -645,7 +645,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
let c = self.new_region_var(c_universe, MiscVariable(origin.span()));
self.combine_map(t).insert(vars, c);
self.undo_log.push(AddCombination(t, vars));
- let new_r = tcx.mk_re_var(c);
+ let new_r = ty::Region::new_var(tcx, c);
for old_r in [a, b] {
match t {
Glb => self.make_subregion(origin.clone(), new_r, old_r),
@@ -683,15 +683,10 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
}
/// See `InferCtxt::region_constraints_added_in_snapshot`.
- pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option<bool> {
+ pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> bool {
self.undo_log
.region_constraints_in_snapshot(mark)
- .map(|&elt| match elt {
- AddConstraint(constraint) => Some(constraint.involves_placeholders()),
- _ => None,
- })
- .max()
- .unwrap_or(None)
+ .any(|&elt| matches!(elt, AddConstraint(_)))
}
#[inline]
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
index ceafafb55..27e1ed56f 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -118,7 +118,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
(&ty::Error(e), _) | (_, &ty::Error(e)) => {
infcx.set_tainted_by_errors(e);
- Ok(self.tcx().ty_error(e))
+ Ok(Ty::new_error(self.tcx(), e))
}
(
@@ -132,7 +132,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local()
- && !self.tcx().trait_solver_next() =>
+ && !self.fields.infcx.next_trait_solver() =>
{
self.fields.obligations.extend(
infcx
diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs
index f7ab05b2d..01c11d163 100644
--- a/compiler/rustc_infer/src/infer/type_variable.rs
+++ b/compiler/rustc_infer/src/infer/type_variable.rs
@@ -123,13 +123,12 @@ pub enum TypeVariableOriginKind {
NormalizeProjectionType,
TypeInference,
OpaqueTypeInference(DefId),
- TypeParameterDefinition(Symbol, Option<DefId>),
+ TypeParameterDefinition(Symbol, DefId),
/// One of the upvars or closure kind parameters in a `ClosureSubsts`
/// (before it has been determined).
// FIXME(eddyb) distinguish upvar inference variables from the rest.
ClosureSynthetic,
- SubstitutionPlaceholder,
AutoDeref,
AdjustmentType,
diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs
index 955c54e85..25d06b21e 100644
--- a/compiler/rustc_infer/src/infer/undo_log.rs
+++ b/compiler/rustc_infer/src/infer/undo_log.rs
@@ -138,11 +138,9 @@ impl<'tcx> InferCtxtInner<'tcx> {
}
if self.undo_log.num_open_snapshots == 1 {
- // The root snapshot. It's safe to clear the undo log because
- // there's no snapshot further out that we might need to roll back
- // to.
+ // After the root snapshot the undo log should be empty.
assert!(snapshot.undo_len == 0);
- self.undo_log.logs.clear();
+ assert!(self.undo_log.logs.is_empty());
}
self.undo_log.num_open_snapshots -= 1;
@@ -183,15 +181,6 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> {
self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
}
- pub(crate) fn region_constraints(
- &self,
- ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
- self.logs.iter().filter_map(|log| match log {
- UndoLog::RegionConstraintCollector(log) => Some(log),
- _ => None,
- })
- }
-
fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
// Failures here may indicate a failure to follow a stack discipline.
assert!(self.logs.len() >= snapshot.undo_len);
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index b5a7d0326..9f440f398 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -25,9 +25,11 @@ impl<'tcx> InferCtxt<'tcx> {
"impl has stricter requirements than trait"
);
- if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
- let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
- err.span_label(span, format!("definition of `{}` from trait", item_name));
+ if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
+ if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+ let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+ err.span_label(span, format!("definition of `{}` from trait", item_name));
+ }
}
err.span_label(error_span, format!("impl has extra requirement {}", requirement));
diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs
index 8ce8b4e20..626dd9359 100644
--- a/compiler/rustc_infer/src/traits/mod.rs
+++ b/compiler/rustc_infer/src/traits/mod.rs
@@ -62,7 +62,8 @@ impl<'tcx, P> From<Obligation<'tcx, P>> for solve::Goal<'tcx, P> {
}
pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
-pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
+pub type TraitObligation<'tcx> = Obligation<'tcx, ty::TraitPredicate<'tcx>>;
+pub type PolyTraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
impl<'tcx> PredicateObligation<'tcx> {
/// Flips the polarity of the inner predicate.
@@ -79,14 +80,14 @@ impl<'tcx> PredicateObligation<'tcx> {
pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> PredicateObligation<'tcx> {
self.param_env = self.param_env.without_const();
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() {
- self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred.without_const()))));
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = self.predicate.kind().skip_binder() && trait_pred.is_const_if_const() {
+ self.predicate = tcx.mk_predicate(self.predicate.kind().map_bound(|_| ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred.without_const()))));
}
self
}
}
-impl<'tcx> TraitObligation<'tcx> {
+impl<'tcx> PolyTraitObligation<'tcx> {
/// Returns `true` if the trait predicate is considered `const` in its ParamEnv.
pub fn is_const(&self) -> bool {
matches!(
@@ -193,7 +194,7 @@ impl<'tcx> FulfillmentError<'tcx> {
}
}
-impl<'tcx> TraitObligation<'tcx> {
+impl<'tcx> PolyTraitObligation<'tcx> {
pub fn polarity(&self) -> ty::ImplPolarity {
self.predicate.skip_binder().polarity
}
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index 74a78f380..074ff7ec9 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -3,7 +3,7 @@ use smallvec::smallvec;
use crate::infer::outlives::components::{push_outlives_components, Component};
use crate::traits::{self, Obligation, PredicateObligation};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
-use rustc_middle::ty::{self, ToPredicate, TyCtxt};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_span::symbol::Ident;
use rustc_span::Span;
@@ -80,14 +80,14 @@ pub struct Elaborator<'tcx, O> {
pub trait Elaboratable<'tcx> {
fn predicate(&self) -> ty::Predicate<'tcx>;
- // Makes a new `Self` but with a different predicate.
- fn child(&self, predicate: ty::Predicate<'tcx>) -> Self;
+ // Makes a new `Self` but with a different clause that comes from elaboration.
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self;
- // Makes a new `Self` but with a different predicate and a different cause
- // code (if `Self` has one).
+ // Makes a new `Self` but with a different clause and a different cause
+ // code (if `Self` has one, such as [`PredicateObligation`]).
fn child_with_derived_cause(
&self,
- predicate: ty::Predicate<'tcx>,
+ clause: ty::Clause<'tcx>,
span: Span,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
index: usize,
@@ -99,18 +99,18 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
self.predicate
}
- fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self {
Obligation {
cause: self.cause.clone(),
param_env: self.param_env,
recursion_depth: 0,
- predicate,
+ predicate: clause.as_predicate(),
}
}
fn child_with_derived_cause(
&self,
- predicate: ty::Predicate<'tcx>,
+ clause: ty::Clause<'tcx>,
span: Span,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
index: usize,
@@ -123,7 +123,12 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
span,
}))
});
- Obligation { cause, param_env: self.param_env, recursion_depth: 0, predicate }
+ Obligation {
+ cause,
+ param_env: self.param_env,
+ recursion_depth: 0,
+ predicate: clause.as_predicate(),
+ }
}
}
@@ -132,18 +137,18 @@ impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> {
*self
}
- fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
- predicate
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+ clause.as_predicate()
}
fn child_with_derived_cause(
&self,
- predicate: ty::Predicate<'tcx>,
+ clause: ty::Clause<'tcx>,
_span: Span,
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
_index: usize,
) -> Self {
- predicate
+ clause.as_predicate()
}
}
@@ -152,18 +157,58 @@ impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
self.0
}
- fn child(&self, predicate: ty::Predicate<'tcx>) -> Self {
- (predicate, self.1)
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+ (clause.as_predicate(), self.1)
+ }
+
+ fn child_with_derived_cause(
+ &self,
+ clause: ty::Clause<'tcx>,
+ _span: Span,
+ _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+ _index: usize,
+ ) -> Self {
+ (clause.as_predicate(), self.1)
+ }
+}
+
+impl<'tcx> Elaboratable<'tcx> for (ty::Clause<'tcx>, Span) {
+ fn predicate(&self) -> ty::Predicate<'tcx> {
+ self.0.as_predicate()
+ }
+
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+ (clause, self.1)
}
fn child_with_derived_cause(
&self,
- predicate: ty::Predicate<'tcx>,
+ clause: ty::Clause<'tcx>,
_span: Span,
_parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
_index: usize,
) -> Self {
- (predicate, self.1)
+ (clause, self.1)
+ }
+}
+
+impl<'tcx> Elaboratable<'tcx> for ty::Clause<'tcx> {
+ fn predicate(&self) -> ty::Predicate<'tcx> {
+ self.as_predicate()
+ }
+
+ fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+ clause
+ }
+
+ fn child_with_derived_cause(
+ &self,
+ clause: ty::Clause<'tcx>,
+ _span: Span,
+ _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+ _index: usize,
+ ) -> Self {
+ clause
}
}
@@ -199,7 +244,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
let bound_predicate = elaboratable.predicate().kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
// Negative trait bounds do not imply any supertrait bounds
if data.polarity == ty::ImplPolarity::Negative {
return;
@@ -212,13 +257,13 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
};
let obligations =
- predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
+ predicates.predicates.iter().enumerate().map(|(index, &(mut clause, span))| {
// when parent predicate is non-const, elaborate it to non-const predicates.
if data.constness == ty::BoundConstness::NotConst {
- pred = pred.without_const(tcx);
+ clause = clause.without_const(tcx);
}
elaboratable.child_with_derived_cause(
- pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
+ clause.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
span,
bound_predicate.rebind(data),
index,
@@ -227,7 +272,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
debug!(?data, ?obligations, "super_predicates");
self.extend_deduped(obligations);
}
- ty::PredicateKind::WellFormed(..) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) => {
// Currently, we do not elaborate WF predicates,
// although we easily could.
}
@@ -243,13 +288,13 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
// Currently, we do not "elaborate" predicates like `X -> Y`,
// though conceivably we might.
}
- ty::PredicateKind::Clause(ty::Clause::Projection(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
// Nothing to elaborate in a projection predicate.
}
ty::PredicateKind::ClosureKind(..) => {
// Nothing to elaborate when waiting for a closure's kind to be inferred.
}
- ty::PredicateKind::ConstEvaluatable(..) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => {
// Currently, we do not elaborate const-evaluatable
// predicates.
}
@@ -257,10 +302,10 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
// Currently, we do not elaborate const-equate
// predicates.
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {
// Nothing to elaborate from `'a: 'b`.
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_max,
r_min,
))) => {
@@ -292,17 +337,15 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
if r.is_late_bound() {
None
} else {
- Some(ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
- ty::OutlivesPredicate(r, r_min),
+ Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
+ r, r_min,
)))
}
}
Component::Param(p) => {
- let ty = tcx.mk_ty_param(p.index, p.name);
- Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty, r_min),
- )))
+ let ty = Ty::new_param(tcx, p.index, p.name);
+ Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
}
Component::UnresolvedInferenceVariable(_) => None,
@@ -310,8 +353,9 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
Component::Alias(alias_ty) => {
// We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
// With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
- Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(alias_ty.to_ty(tcx), r_min),
+ Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+ alias_ty.to_ty(tcx),
+ r_min,
)))
}
@@ -321,20 +365,16 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
None
}
})
- .map(|predicate_kind| {
- bound_predicate.rebind(predicate_kind).to_predicate(tcx)
- })
- .map(|predicate| elaboratable.child(predicate)),
+ .map(|clause| {
+ elaboratable.child(bound_predicate.rebind(clause).to_predicate(tcx))
+ }),
);
}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- // Nothing to elaborate
- }
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::AliasRelate(..) => {
// No
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) => {
// Nothing to elaborate
}
}
@@ -400,7 +440,7 @@ pub fn transitive_bounds_that_define_assoc_item<'tcx>(
tcx.super_predicates_that_define_assoc_item((trait_ref.def_id(), assoc_name));
for (super_predicate, _) in super_predicates.predicates {
let subst_predicate = super_predicate.subst_supertrait(tcx, &trait_ref);
- if let Some(binder) = subst_predicate.to_opt_poly_trait_pred() {
+ if let Some(binder) = subst_predicate.as_trait_clause() {
stack.push(binder.map_bound(|t| t.trait_ref));
}
}
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index 2c7438ed9..7826d42dc 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
[lib]
[dependencies]
+atty = "0.2.13"
libloading = "0.7.1"
tracing = "0.1"
rustc-rayon-core = { version = "0.5.0", optional = true }
diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl
index be1a75f02..bd9fad8b0 100644
--- a/compiler/rustc_interface/messages.ftl
+++ b/compiler/rustc_interface/messages.ftl
@@ -33,6 +33,7 @@ interface_mixed_proc_macro_crate =
interface_multiple_output_types_adaption =
due to multiple output types requested, the explicitly specified output file name will be adapted for each output type
+interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout
interface_out_dir_error =
failed to find or create the directory specified by `--out-dir`
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs
index 0eedee250..a9ab2720d 100644
--- a/compiler/rustc_interface/src/errors.rs
+++ b/compiler/rustc_interface/src/errors.rs
@@ -108,3 +108,7 @@ pub struct IgnoringExtraFilename;
#[derive(Diagnostic)]
#[diag(interface_ignoring_out_dir)]
pub struct IgnoringOutDir;
+
+#[derive(Diagnostic)]
+#[diag(interface_multiple_output_types_to_stdout)]
+pub struct MultipleOutputTypesToStdout;
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 39d568979..953c2e4b8 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -14,12 +14,11 @@ use rustc_middle::{bug, ty};
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_query_impl::QueryCtxt;
use rustc_query_system::query::print_query_stack;
-use rustc_session::config::{self, ErrorOutputType, Input, OutputFilenames};
-use rustc_session::config::{CheckCfg, ExpectedValues};
-use rustc_session::lint;
+use rustc_session::config::{self, CheckCfg, ExpectedValues, Input, OutFileName, OutputFilenames};
use rustc_session::parse::{CrateConfig, ParseSess};
+use rustc_session::CompilerIO;
use rustc_session::Session;
-use rustc_session::{early_error, CompilerIO};
+use rustc_session::{lint, EarlyErrorHandler};
use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
use std::path::PathBuf;
@@ -36,7 +35,7 @@ pub type Result<T> = result::Result<T, ErrorGuaranteed>;
/// Created by passing [`Config`] to [`run_compiler`].
pub struct Compiler {
pub(crate) sess: Lrc<Session>,
- codegen_backend: Lrc<Box<dyn CodegenBackend>>,
+ codegen_backend: Lrc<dyn CodegenBackend>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries: Option<fn(&Session, &mut Providers, &mut ExternProviders)>,
}
@@ -45,7 +44,7 @@ impl Compiler {
pub fn session(&self) -> &Lrc<Session> {
&self.sess
}
- pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+ pub fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> {
&self.codegen_backend
}
pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> {
@@ -66,7 +65,10 @@ pub fn set_thread_safe_mode(sopts: &config::UnstableOptions) {
}
/// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
-pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
+pub fn parse_cfgspecs(
+ handler: &EarlyErrorHandler,
+ cfgspecs: Vec<String>,
+) -> FxHashSet<(String, Option<String>)> {
rustc_span::create_default_session_if_not_set_then(move |_| {
let cfg = cfgspecs
.into_iter()
@@ -78,10 +80,10 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
macro_rules! error {
($reason: expr) => {
- early_error(
- ErrorOutputType::default(),
- format!(concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), s),
- );
+ handler.early_error(format!(
+ concat!("invalid `--cfg` argument: `{}` (", $reason, ")"),
+ s
+ ));
};
}
@@ -125,7 +127,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
}
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
-pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
+pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
rustc_span::create_default_session_if_not_set_then(move |_| {
let mut check_cfg = CheckCfg::default();
@@ -137,10 +139,10 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
macro_rules! error {
($reason: expr) => {
- early_error(
- ErrorOutputType::default(),
- format!(concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"), s),
- )
+ handler.early_error(format!(
+ concat!("invalid `--check-cfg` argument: `{}` (", $reason, ")"),
+ s
+ ))
};
}
@@ -252,7 +254,7 @@ pub struct Config {
pub input: Input,
pub output_dir: Option<PathBuf>,
- pub output_file: Option<PathBuf>,
+ pub output_file: Option<OutFileName>,
pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
pub locale_resources: &'static [&'static str],
@@ -294,8 +296,11 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let registry = &config.registry;
+ let handler = EarlyErrorHandler::new(config.opts.error_format);
+
let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let (mut sess, codegen_backend) = util::create_session(
+ &handler,
config.opts,
config.crate_cfg,
config.crate_check_cfg,
@@ -318,7 +323,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
let compiler = Compiler {
sess: Lrc::new(sess),
- codegen_backend: Lrc::new(codegen_backend),
+ codegen_backend: Lrc::from(codegen_backend),
register_lints: config.register_lints,
override_queries: config.override_queries,
};
@@ -333,6 +338,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
};
let prof = compiler.sess.prof.clone();
+
prof.generic_activity("drop_compiler").run(move || drop(compiler));
r
})
@@ -348,7 +354,12 @@ pub fn try_print_query_stack(handler: &Handler, num_frames: Option<usize>) {
// state if it was responsible for triggering the panic.
let i = ty::tls::with_context_opt(|icx| {
if let Some(icx) = icx {
- print_query_stack(QueryCtxt::new(icx.tcx), icx.query, handler, num_frames)
+ ty::print::with_no_queries!(print_query_stack(
+ QueryCtxt::new(icx.tcx),
+ icx.query,
+ handler,
+ num_frames
+ ))
} else {
0
}
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 42d8d2280..6b3facd04 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -24,7 +24,8 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a
use rustc_passes::{self, hir_stats, layout_test};
use rustc_plugin_impl as plugin;
use rustc_resolve::Resolver;
-use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
+use rustc_session::code_stats::VTableSizeInfo;
+use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use rustc_session::cstore::{MetadataLoader, Untracked};
use rustc_session::output::filename_for_input;
use rustc_session::search_paths::PathKind;
@@ -373,19 +374,23 @@ fn generated_output_paths(
) -> Vec<PathBuf> {
let mut out_filenames = Vec::new();
for output_type in sess.opts.output_types.keys() {
- let file = outputs.path(*output_type);
+ let out_filename = outputs.path(*output_type);
+ let file = out_filename.as_path().to_path_buf();
match *output_type {
// If the filename has been overridden using `-o`, it will not be modified
// by appending `.rlib`, `.exe`, etc., so we can skip this transformation.
OutputType::Exe if !exact_name => {
for crate_type in sess.crate_types().iter() {
let p = filename_for_input(sess, *crate_type, crate_name, outputs);
- out_filenames.push(p);
+ out_filenames.push(p.as_path().to_path_buf());
}
}
OutputType::DepInfo if sess.opts.unstable_opts.dep_info_omit_d_target => {
// Don't add the dep-info output when omitting it from dep-info targets
}
+ OutputType::DepInfo if out_filename.is_stdout() => {
+ // Don't add the dep-info output when it goes to stdout
+ }
_ => {
out_filenames.push(file);
}
@@ -452,7 +457,8 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
if !sess.opts.output_types.contains_key(&OutputType::DepInfo) {
return;
}
- let deps_filename = outputs.path(OutputType::DepInfo);
+ let deps_output = outputs.path(OutputType::DepInfo);
+ let deps_filename = deps_output.as_path();
let result: io::Result<()> = try {
// Build a list of files used to compile the output and
@@ -515,33 +521,47 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P
}
}
- let mut file = BufWriter::new(fs::File::create(&deps_filename)?);
- for path in out_filenames {
- writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
- }
+ let write_deps_to_file = |file: &mut dyn Write| -> io::Result<()> {
+ for path in out_filenames {
+ writeln!(file, "{}: {}\n", path.display(), files.join(" "))?;
+ }
- // Emit a fake target for each input file to the compilation. This
- // prevents `make` from spitting out an error if a file is later
- // deleted. For more info see #28735
- for path in files {
- writeln!(file, "{path}:")?;
- }
+ // Emit a fake target for each input file to the compilation. This
+ // prevents `make` from spitting out an error if a file is later
+ // deleted. For more info see #28735
+ for path in files {
+ writeln!(file, "{path}:")?;
+ }
- // Emit special comments with information about accessed environment variables.
- let env_depinfo = sess.parse_sess.env_depinfo.borrow();
- if !env_depinfo.is_empty() {
- let mut envs: Vec<_> = env_depinfo
- .iter()
- .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
- .collect();
- envs.sort_unstable();
- writeln!(file)?;
- for (k, v) in envs {
- write!(file, "# env-dep:{k}")?;
- if let Some(v) = v {
- write!(file, "={v}")?;
- }
+ // Emit special comments with information about accessed environment variables.
+ let env_depinfo = sess.parse_sess.env_depinfo.borrow();
+ if !env_depinfo.is_empty() {
+ let mut envs: Vec<_> = env_depinfo
+ .iter()
+ .map(|(k, v)| (escape_dep_env(*k), v.map(escape_dep_env)))
+ .collect();
+ envs.sort_unstable();
writeln!(file)?;
+ for (k, v) in envs {
+ write!(file, "# env-dep:{k}")?;
+ if let Some(v) = v {
+ write!(file, "={v}")?;
+ }
+ writeln!(file)?;
+ }
+ }
+
+ Ok(())
+ };
+
+ match deps_output {
+ OutFileName::Stdout => {
+ let mut file = BufWriter::new(io::stdout());
+ write_deps_to_file(&mut file)?;
+ }
+ OutFileName::Real(ref path) => {
+ let mut file = BufWriter::new(fs::File::create(path)?);
+ write_deps_to_file(&mut file)?;
}
}
};
@@ -720,8 +740,8 @@ pub fn create_global_ctxt<'tcx>(
})
}
-/// Runs the resolution, type-checking, region checking and other
-/// miscellaneous analysis passes on the crate.
+/// Runs the type-checking, region checking and other miscellaneous analysis
+/// passes on the crate.
fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
rustc_passes::hir_id_validator::check_crate(tcx);
@@ -847,6 +867,92 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
sess.time("check_lint_expectations", || tcx.check_expectations(None));
});
+ if sess.opts.unstable_opts.print_vtable_sizes {
+ let traits = tcx.traits(LOCAL_CRATE);
+
+ for &tr in traits {
+ if !tcx.check_is_object_safe(tr) {
+ continue;
+ }
+
+ let name = ty::print::with_no_trimmed_paths!(tcx.def_path_str(tr));
+
+ let mut first_dsa = true;
+
+ // Number of vtable entries, if we didn't have upcasting
+ let mut entries_ignoring_upcasting = 0;
+ // Number of vtable entries needed solely for upcasting
+ let mut entries_for_upcasting = 0;
+
+ let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, tr));
+
+ // A slightly edited version of the code in `rustc_trait_selection::traits::vtable::vtable_entries`,
+ // that works without self type and just counts number of entries.
+ //
+ // Note that this is technically wrong, for traits which have associated types in supertraits:
+ //
+ // trait A: AsRef<Self::T> + AsRef<()> { type T; }
+ //
+ // Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>` and
+ // `AsRef<()>` are the same trait, thus we assume that those are different, and potentially
+ // over-estimate how many vtable entries there are.
+ //
+ // Similarly this is wrong for traits that have methods with possibly-impossible bounds.
+ // For example:
+ //
+ // trait B<T> { fn f(&self) where T: Copy; }
+ //
+ // Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3.
+ // However, since we don't know `T`, we can't know if `T: Copy` holds or not,
+ // thus we lean on the bigger side and say it has 4 entries.
+ traits::vtable::prepare_vtable_segments(tcx, trait_ref, |segment| {
+ match segment {
+ traits::vtable::VtblSegment::MetadataDSA => {
+ // If this is the first dsa, it would be included either way,
+ // otherwise it's needed for upcasting
+ if std::mem::take(&mut first_dsa) {
+ entries_ignoring_upcasting += 3;
+ } else {
+ entries_for_upcasting += 3;
+ }
+ }
+
+ traits::vtable::VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ // Lookup the shape of vtable for the trait.
+ let own_existential_entries =
+ tcx.own_existential_vtable_entries(trait_ref.def_id());
+
+ // The original code here ignores the method if its predicates are impossible.
+ // We can't really do that as, for example, all not trivial bounds on generic
+ // parameters are impossible (since we don't know the parameters...),
+ // see the comment above.
+ entries_ignoring_upcasting += own_existential_entries.len();
+
+ if emit_vptr {
+ entries_for_upcasting += 1;
+ }
+ }
+ }
+
+ std::ops::ControlFlow::Continue::<std::convert::Infallible>(())
+ });
+
+ sess.code_stats.record_vtable_size(
+ tr,
+ &name,
+ VTableSizeInfo {
+ trait_name: name.clone(),
+ entries: entries_ignoring_upcasting + entries_for_upcasting,
+ entries_ignoring_upcasting,
+ entries_for_upcasting,
+ upcasting_cost_percent: entries_for_upcasting as f64
+ / entries_ignoring_upcasting as f64
+ * 100.,
+ },
+ )
+ }
+ }
+
Ok(())
}
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index c441a8ffd..8c4cdc669 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -5,6 +5,7 @@ use crate::passes;
use rustc_ast as ast;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_codegen_ssa::CodegenResults;
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::steal::Steal;
use rustc_data_structures::svh::Svh;
use rustc_data_structures::sync::{AppendOnlyIndexVec, Lrc, OnceCell, RwLock, WorkerLocal};
@@ -92,7 +93,6 @@ pub struct Queries<'tcx> {
dep_graph: Query<DepGraph>,
// This just points to what's in `gcx_cell`.
gcx: Query<&'tcx GlobalCtxt<'tcx>>,
- ongoing_codegen: Query<Box<dyn Any>>,
}
impl<'tcx> Queries<'tcx> {
@@ -109,14 +109,13 @@ impl<'tcx> Queries<'tcx> {
register_plugins: Default::default(),
dep_graph: Default::default(),
gcx: Default::default(),
- ongoing_codegen: Default::default(),
}
}
fn session(&self) -> &Lrc<Session> {
&self.compiler.sess
}
- fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
+ fn codegen_backend(&self) -> &Lrc<dyn CodegenBackend> {
self.compiler.codegen_backend()
}
@@ -193,9 +192,15 @@ impl<'tcx> Queries<'tcx> {
let future_opt = self.dep_graph_future()?.steal();
let dep_graph = future_opt
.and_then(|future| {
- let (prev_graph, prev_work_products) =
+ let (prev_graph, mut prev_work_products) =
sess.time("blocked_on_dep_graph_loading", || future.open().open(sess));
-
+ // Convert from UnordMap to FxIndexMap by sorting
+ let prev_work_product_ids =
+ prev_work_products.items().map(|x| *x.0).into_sorted_stable_ord();
+ let prev_work_products = prev_work_product_ids
+ .into_iter()
+ .map(|x| (x, prev_work_products.remove(&x).unwrap()))
+ .collect::<FxIndexMap<_, _>>();
rustc_incremental::build_dep_graph(sess, prev_graph, prev_work_products)
})
.unwrap_or_else(DepGraph::new_disabled);
@@ -242,23 +247,19 @@ impl<'tcx> Queries<'tcx> {
})
}
- pub fn ongoing_codegen(&'tcx self) -> Result<QueryResult<'_, Box<dyn Any>>> {
- self.ongoing_codegen.compute(|| {
- self.global_ctxt()?.enter(|tcx| {
- tcx.analysis(()).ok();
-
- // Don't do code generation if there were any errors
- self.session().compile_status()?;
+ pub fn ongoing_codegen(&'tcx self) -> Result<Box<dyn Any>> {
+ self.global_ctxt()?.enter(|tcx| {
+ // Don't do code generation if there were any errors
+ self.session().compile_status()?;
- // If we have any delayed bugs, for example because we created TyKind::Error earlier,
- // it's likely that codegen will only cause more ICEs, obscuring the original problem
- self.session().diagnostic().flush_delayed();
+ // If we have any delayed bugs, for example because we created TyKind::Error earlier,
+ // it's likely that codegen will only cause more ICEs, obscuring the original problem
+ self.session().diagnostic().flush_delayed();
- // Hook for UI tests.
- Self::check_for_rustc_errors_attr(tcx);
+ // Hook for UI tests.
+ Self::check_for_rustc_errors_attr(tcx);
- Ok(passes::start_codegen(&***self.codegen_backend(), tcx))
- })
+ Ok(passes::start_codegen(&**self.codegen_backend(), tcx))
})
}
@@ -296,7 +297,7 @@ impl<'tcx> Queries<'tcx> {
}
}
- pub fn linker(&'tcx self) -> Result<Linker> {
+ pub fn linker(&'tcx self, ongoing_codegen: Box<dyn Any>) -> Result<Linker> {
let sess = self.session().clone();
let codegen_backend = self.codegen_backend().clone();
@@ -307,7 +308,6 @@ impl<'tcx> Queries<'tcx> {
tcx.dep_graph.clone(),
)
});
- let ongoing_codegen = self.ongoing_codegen()?.steal();
Ok(Linker {
sess,
@@ -324,7 +324,7 @@ impl<'tcx> Queries<'tcx> {
pub struct Linker {
// compilation inputs
sess: Lrc<Session>,
- codegen_backend: Lrc<Box<dyn CodegenBackend>>,
+ codegen_backend: Lrc<dyn CodegenBackend>,
// compilation outputs
dep_graph: DepGraph,
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 28e719a40..09141afd1 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -8,10 +8,11 @@ use rustc_session::config::rustc_optgroups;
use rustc_session::config::DebugInfo;
use rustc_session::config::Input;
use rustc_session::config::InstrumentXRay;
+use rustc_session::config::LinkSelfContained;
use rustc_session::config::TraitSolver;
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
use rustc_session::config::{
- BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet,
+ BranchProtection, Externs, OomStrategy, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet,
ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel,
};
use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath};
@@ -21,8 +22,8 @@ use rustc_session::config::{InstrumentCoverage, Passes};
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
-use rustc_session::CompilerIO;
use rustc_session::{build_session, getopts, Session};
+use rustc_session::{CompilerIO, EarlyErrorHandler};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
use rustc_span::FileName;
@@ -36,15 +37,18 @@ use std::path::{Path, PathBuf};
type CfgSpecs = FxHashSet<(String, Option<String>)>;
-fn build_session_options_and_crate_config(matches: getopts::Matches) -> (Options, CfgSpecs) {
- let sessopts = build_session_options(&matches);
- let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
+fn build_session_options_and_crate_config(
+ handler: &mut EarlyErrorHandler,
+ matches: getopts::Matches,
+) -> (Options, CfgSpecs) {
+ let sessopts = build_session_options(handler, &matches);
+ let cfg = parse_cfgspecs(handler, matches.opt_strs("cfg"));
(sessopts, cfg)
}
-fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
+fn mk_session(handler: &mut EarlyErrorHandler, matches: getopts::Matches) -> (Session, CfgSpecs) {
let registry = registry::Registry::new(&[]);
- let (sessopts, cfg) = build_session_options_and_crate_config(matches);
+ let (sessopts, cfg) = build_session_options_and_crate_config(handler, matches);
let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let io = CompilerIO {
input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
@@ -52,8 +56,18 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
output_file: None,
temps_dir,
};
- let sess =
- build_session(sessopts, io, None, registry, vec![], Default::default(), None, None, "");
+ let sess = build_session(
+ handler,
+ sessopts,
+ io,
+ None,
+ registry,
+ vec![],
+ Default::default(),
+ None,
+ None,
+ "",
+ );
(sess, cfg)
}
@@ -120,7 +134,8 @@ fn assert_non_crate_hash_different(x: &Options, y: &Options) {
fn test_switch_implies_cfg_test() {
rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["--test".to_string()]).unwrap();
- let (sess, cfg) = mk_session(matches);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let (sess, cfg) = mk_session(&mut handler, matches);
let cfg = build_configuration(&sess, to_crate_config(cfg));
assert!(cfg.contains(&(sym::test, None)));
});
@@ -131,7 +146,8 @@ fn test_switch_implies_cfg_test() {
fn test_switch_implies_cfg_test_unless_cfg_test() {
rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
- let (sess, cfg) = mk_session(matches);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let (sess, cfg) = mk_session(&mut handler, matches);
let cfg = build_configuration(&sess, to_crate_config(cfg));
let mut test_items = cfg.iter().filter(|&&(name, _)| name == sym::test);
assert!(test_items.next().is_some());
@@ -143,20 +159,23 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
fn test_can_print_warnings() {
rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
- let (sess, _) = mk_session(matches);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let (sess, _) = mk_session(&mut handler, matches);
assert!(!sess.diagnostic().can_emit_warnings());
});
rustc_span::create_default_session_globals_then(|| {
let matches =
optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
- let (sess, _) = mk_session(matches);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let (sess, _) = mk_session(&mut handler, matches);
assert!(sess.diagnostic().can_emit_warnings());
});
rustc_span::create_default_session_globals_then(|| {
let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
- let (sess, _) = mk_session(matches);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+ let (sess, _) = mk_session(&mut handler, matches);
assert!(sess.diagnostic().can_emit_warnings());
});
}
@@ -167,8 +186,14 @@ fn test_output_types_tracking_hash_different_paths() {
let mut v2 = Options::default();
let mut v3 = Options::default();
- v1.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("./some/thing")))]);
- v2.output_types = OutputTypes::new(&[(OutputType::Exe, Some(PathBuf::from("/some/thing")))]);
+ v1.output_types = OutputTypes::new(&[(
+ OutputType::Exe,
+ Some(OutFileName::Real(PathBuf::from("./some/thing"))),
+ )]);
+ v2.output_types = OutputTypes::new(&[(
+ OutputType::Exe,
+ Some(OutFileName::Real(PathBuf::from("/some/thing"))),
+ )]);
v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]);
assert_non_crate_hash_different(&v1, &v2);
@@ -182,13 +207,13 @@ fn test_output_types_tracking_hash_different_construction_order() {
let mut v2 = Options::default();
v1.output_types = OutputTypes::new(&[
- (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
- (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
+ (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))),
+ (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))),
]);
v2.output_types = OutputTypes::new(&[
- (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))),
- (OutputType::Exe, Some(PathBuf::from("./some/thing"))),
+ (OutputType::Bitcode, Some(OutFileName::Real(PathBuf::from("./some/thing.bc")))),
+ (OutputType::Exe, Some(OutFileName::Real(PathBuf::from("./some/thing")))),
]);
assert_same_hash(&v1, &v2);
@@ -296,35 +321,36 @@ fn test_search_paths_tracking_hash_different_order() {
let mut v3 = Options::default();
let mut v4 = Options::default();
+ let handler = EarlyErrorHandler::new(JSON);
const JSON: ErrorOutputType = ErrorOutputType::Json {
pretty: false,
json_rendered: HumanReadableErrorType::Default(ColorConfig::Never),
};
// Reference
- v1.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
- v1.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
- v1.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
- v1.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
- v1.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
- v2.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
- v2.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
- v2.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
- v2.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
- v2.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
- v3.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
- v3.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
- v3.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
- v3.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
- v3.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
-
- v4.search_paths.push(SearchPath::from_cli_opt("all=mno", JSON));
- v4.search_paths.push(SearchPath::from_cli_opt("native=abc", JSON));
- v4.search_paths.push(SearchPath::from_cli_opt("crate=def", JSON));
- v4.search_paths.push(SearchPath::from_cli_opt("dependency=ghi", JSON));
- v4.search_paths.push(SearchPath::from_cli_opt("framework=jkl", JSON));
+ v1.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+ v1.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+ v1.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+ v1.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+ v1.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+ v2.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+ v2.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+ v2.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+ v2.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+ v2.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+ v3.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+ v3.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
+ v3.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+ v3.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+ v3.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+
+ v4.search_paths.push(SearchPath::from_cli_opt(&handler, "all=mno"));
+ v4.search_paths.push(SearchPath::from_cli_opt(&handler, "native=abc"));
+ v4.search_paths.push(SearchPath::from_cli_opt(&handler, "crate=def"));
+ v4.search_paths.push(SearchPath::from_cli_opt(&handler, "dependency=ghi"));
+ v4.search_paths.push(SearchPath::from_cli_opt(&handler, "framework=jkl"));
assert_same_hash(&v1, &v2);
assert_same_hash(&v1, &v3);
@@ -554,7 +580,7 @@ fn test_codegen_options_tracking_hash() {
untracked!(incremental, Some(String::from("abc")));
// `link_arg` is omitted because it just forwards to `link_args`.
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
- untracked!(link_self_contained, Some(true));
+ untracked!(link_self_contained, LinkSelfContained::on());
untracked!(linker, Some(PathBuf::from("linker")));
untracked!(linker_flavor, Some(LinkerFlavorCli::Gcc));
untracked!(no_stack_check, true);
@@ -679,7 +705,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(ls, true);
untracked!(macro_backtrace, true);
untracked!(meta_stats, true);
- untracked!(mir_pretty_relative_line_numbers, true);
+ untracked!(mir_include_spans, true);
untracked!(nll_facts, true);
untracked!(no_analysis, true);
untracked!(no_leak_check, true);
@@ -815,7 +841,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(thir_unsafeck, true);
tracked!(tiny_const_eval_limit, true);
tracked!(tls_model, Some(TlsModel::GeneralDynamic));
- tracked!(trait_solver, TraitSolver::Chalk);
+ tracked!(trait_solver, TraitSolver::NextCoherence);
tracked!(translate_remapped_path_to_local_path, false);
tracked!(trap_unreachable, Some(false));
tracked!(treat_err_as_bug, NonZeroUsize::new(1));
@@ -845,7 +871,9 @@ fn test_edition_parsing() {
let options = Options::default();
assert!(options.edition == DEFAULT_EDITION);
+ let mut handler = EarlyErrorHandler::new(ErrorOutputType::default());
+
let matches = optgroups().parse(&["--edition=2018".to_string()]).unwrap();
- let (sessopts, _) = build_session_options_and_crate_config(matches);
+ let (sessopts, _) = build_session_options_and_crate_config(&mut handler, matches);
assert!(sessopts.edition == Edition::Edition2018)
}
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index cb1975020..035ea2414 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -11,16 +11,16 @@ use rustc_parse::validate_attr;
use rustc_session as session;
use rustc_session::config::CheckCfg;
use rustc_session::config::{self, CrateType};
-use rustc_session::config::{ErrorOutputType, OutputFilenames};
+use rustc_session::config::{OutFileName, OutputFilenames, OutputTypes};
use rustc_session::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::CrateConfig;
-use rustc_session::{early_error, filesearch, output, Session};
+use rustc_session::{filesearch, output, Session};
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileLoader;
use rustc_span::symbol::{sym, Symbol};
-use session::CompilerIO;
+use session::{CompilerIO, EarlyErrorHandler};
use std::env;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::mem;
@@ -58,6 +58,7 @@ pub fn add_configuration(
}
pub fn create_session(
+ handler: &EarlyErrorHandler,
sopts: config::Options,
cfg: FxHashSet<(String, Option<String>)>,
check_cfg: CheckCfg,
@@ -73,7 +74,11 @@ pub fn create_session(
let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
make_codegen_backend(&sopts)
} else {
- get_codegen_backend(&sopts.maybe_sysroot, sopts.unstable_opts.codegen_backend.as_deref())
+ get_codegen_backend(
+ handler,
+ &sopts.maybe_sysroot,
+ sopts.unstable_opts.codegen_backend.as_deref(),
+ )
};
// target_override is documented to be called before init(), so this is okay
@@ -88,7 +93,7 @@ pub fn create_session(
) {
Ok(bundle) => bundle,
Err(e) => {
- early_error(sopts.error_format, format!("failed to load fluent bundle: {e}"));
+ handler.early_error(format!("failed to load fluent bundle: {e}"));
}
};
@@ -96,6 +101,7 @@ pub fn create_session(
locale_resources.push(codegen_backend.locale_resource());
let mut sess = session::build_session(
+ handler,
sopts,
io,
bundle,
@@ -218,16 +224,16 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
})
}
-fn load_backend_from_dylib(path: &Path) -> MakeBackendFn {
+fn load_backend_from_dylib(handler: &EarlyErrorHandler, path: &Path) -> MakeBackendFn {
let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
let err = format!("couldn't load codegen backend {path:?}: {err}");
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
});
let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
.unwrap_or_else(|e| {
let err = format!("couldn't load codegen backend: {e}");
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
});
// Intentionally leak the dynamic library. We can't ever unload it
@@ -242,6 +248,7 @@ fn load_backend_from_dylib(path: &Path) -> MakeBackendFn {
///
/// A name of `None` indicates that the default backend should be used.
pub fn get_codegen_backend(
+ handler: &EarlyErrorHandler,
maybe_sysroot: &Option<PathBuf>,
backend_name: Option<&str>,
) -> Box<dyn CodegenBackend> {
@@ -251,10 +258,12 @@ pub fn get_codegen_backend(
let default_codegen_backend = option_env!("CFG_DEFAULT_CODEGEN_BACKEND").unwrap_or("llvm");
match backend_name.unwrap_or(default_codegen_backend) {
- filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()),
+ filename if filename.contains('.') => {
+ load_backend_from_dylib(handler, filename.as_ref())
+ }
#[cfg(feature = "llvm")]
"llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new,
- backend_name => get_codegen_sysroot(maybe_sysroot, backend_name),
+ backend_name => get_codegen_sysroot(handler, maybe_sysroot, backend_name),
}
});
@@ -286,7 +295,11 @@ fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
})
}
-fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn {
+fn get_codegen_sysroot(
+ handler: &EarlyErrorHandler,
+ maybe_sysroot: &Option<PathBuf>,
+ backend_name: &str,
+) -> MakeBackendFn {
// For now we only allow this function to be called once as it'll dlopen a
// few things, which seems to work best if we only do that once. In
// general this assertion never trips due to the once guard in `get_codegen_backend`,
@@ -321,7 +334,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
"failed to find a `codegen-backends` folder \
in the sysroot candidates:\n* {candidates}"
);
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
});
info!("probing {} for a codegen backend", sysroot.display());
@@ -332,7 +345,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
sysroot.display(),
e
);
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
});
let mut file: Option<PathBuf> = None;
@@ -360,16 +373,16 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M
prev.display(),
path.display()
);
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
}
file = Some(path.clone());
}
match file {
- Some(ref s) => load_backend_from_dylib(s),
+ Some(ref s) => load_backend_from_dylib(handler, s),
None => {
let err = format!("unsupported builtin codegen backend `{backend_name}`");
- early_error(ErrorOutputType::default(), err);
+ handler.early_error(err);
}
}
}
@@ -500,7 +513,36 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
base
}
+fn multiple_output_types_to_stdout(
+ output_types: &OutputTypes,
+ single_output_file_is_stdout: bool,
+) -> bool {
+ if atty::is(atty::Stream::Stdout) {
+ // If stdout is a tty, check if multiple text output types are
+ // specified by `--emit foo=- --emit bar=-` or `-o - --emit foo,bar`
+ let named_text_types = output_types
+ .iter()
+ .filter(|(f, o)| f.is_text_output() && *o == &Some(OutFileName::Stdout))
+ .count();
+ let unnamed_text_types =
+ output_types.iter().filter(|(f, o)| f.is_text_output() && o.is_none()).count();
+ named_text_types > 1 || unnamed_text_types > 1 && single_output_file_is_stdout
+ } else {
+ // Otherwise, all the output types should be checked
+ let named_types =
+ output_types.values().filter(|o| *o == &Some(OutFileName::Stdout)).count();
+ let unnamed_types = output_types.values().filter(|o| o.is_none()).count();
+ named_types > 1 || unnamed_types > 1 && single_output_file_is_stdout
+ }
+}
+
pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
+ if multiple_output_types_to_stdout(
+ &sess.opts.output_types,
+ sess.io.output_file == Some(OutFileName::Stdout),
+ ) {
+ sess.emit_fatal(errors::MultipleOutputTypesToStdout);
+ }
match sess.io.output_file {
None => {
// "-" as input file will cause the parser to read from stdin so we
@@ -544,7 +586,7 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
OutputFilenames::new(
out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
- out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
+ out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(),
ofile,
sess.io.temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 0fa67cdb3..22e22c833 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -262,8 +262,6 @@ lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
lint_improper_ctypes_char_reason = the `char` type has no C equivalent
lint_improper_ctypes_dyn = trait objects have no C equivalent
-lint_improper_ctypes_enum_phantomdata = this enum contains a PhantomData field
-
lint_improper_ctypes_enum_repr_help =
consider adding a `#[repr(C)]`, `#[repr(transparent)]`, or integer `#[repr(...)]` attribute to this enum
@@ -304,6 +302,21 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
lint_improper_ctypes_union_layout_reason = this union has unspecified layout
lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
+# FIXME: we should ordinalize $valid_up_to when we add support for doing so
+lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal always return an error
+ .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes
+
+# FIXME: we should ordinalize $valid_up_to when we add support for doing so
+lint_invalid_from_utf8_unchecked = calls to `{$method}` with a invalid literal are undefined behavior
+ .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes
+
+lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be directly compared to itself
+ .suggestion = use `f32::is_nan()` or `f64::is_nan()` instead
+
+lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
+
+lint_invalid_reference_casting = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
+
lint_lintpass_by_hand = implementing `LintPass` by hand
.help = try using `declare_lint_pass!` or `impl_lint_pass!` instead
@@ -410,6 +423,7 @@ lint_overflowing_bin_hex = literal out of range for `{$ty}`
.negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}`
.positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`
.suggestion = consider using the type `{$suggestion_ty}` instead
+ .sign_bit_suggestion = to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}`
.help = consider using the type `{$suggestion_ty}` instead
lint_overflowing_int = literal out of range for `{$ty}`
@@ -480,6 +494,10 @@ lint_tykind = usage of `ty::TyKind`
lint_tykind_kind = usage of `ty::TyKind::<kind>`
.suggestion = try using `ty::<kind>` directly
+lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of the inner value does nothing
+ .label = argument has type `{$arg_ty}`
+ .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value
+
lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op
.label = this function will not propagate the caller location
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 85141836e..b821933e9 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -286,7 +286,9 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
}
declare_lint! {
- /// The `unsafe_code` lint catches usage of `unsafe` code.
+ /// The `unsafe_code` lint catches usage of `unsafe` code and other
+ /// potentially unsound constructs like `no_mangle`, `export_name`,
+ /// and `link_section`.
///
/// ### Example
///
@@ -297,17 +299,29 @@ declare_lint! {
///
/// }
/// }
+ ///
+ /// #[no_mangle]
+ /// fn func_0() { }
+ ///
+ /// #[export_name = "exported_symbol_name"]
+ /// pub fn name_in_rust() { }
+ ///
+ /// #[no_mangle]
+ /// #[link_section = ".example_section"]
+ /// pub static VAR1: u32 = 1;
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
- /// This lint is intended to restrict the usage of `unsafe`, which can be
- /// difficult to use correctly.
+ /// This lint is intended to restrict the usage of `unsafe` blocks and other
+ /// constructs (including, but not limited to `no_mangle`, `link_section`
+ /// and `export_name` attributes) wrong usage of which causes undefined
+ /// behavior.
UNSAFE_CODE,
Allow,
- "usage of `unsafe` code"
+ "usage of `unsafe` code and other potentially unsound constructs"
}
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
@@ -651,9 +665,7 @@ declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS])
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
- && cx.tcx.local_visibility(item.owner_id.def_id).is_public())
- {
+ if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
return;
}
let (def, ty) = match item.kind {
@@ -662,21 +674,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
return;
}
let def = cx.tcx.adt_def(item.owner_id);
- (def, cx.tcx.mk_adt(def, ty::List::empty()))
+ (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
}
hir::ItemKind::Union(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
return;
}
let def = cx.tcx.adt_def(item.owner_id);
- (def, cx.tcx.mk_adt(def, ty::List::empty()))
+ (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
}
hir::ItemKind::Enum(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
return;
}
let def = cx.tcx.adt_def(item.owner_id);
- (def, cx.tcx.mk_adt(def, ty::List::empty()))
+ (def, Ty::new_adt(cx.tcx, def, ty::List::empty()))
}
_ => return,
};
@@ -772,9 +784,7 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
- && cx.tcx.local_visibility(item.owner_id.def_id).is_public())
- {
+ if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
return;
}
@@ -1321,10 +1331,14 @@ declare_lint! {
///
/// ### Explanation
///
- /// A bare `pub` visibility may be misleading if the item is not actually
- /// publicly exported from the crate. The `pub(crate)` visibility is
- /// recommended to be used instead, which more clearly expresses the intent
- /// that the item is only visible within its own crate.
+ /// The `pub` keyword both expresses an intent for an item to be publicly available, and also
+ /// signals to the compiler to make the item publicly accessible. The intent can only be
+ /// satisfied, however, if all items which contain this item are *also* publicly accessible.
+ /// Thus, this lint serves to identify situations where the intent does not match the reality.
+ ///
+ /// If you wish the item to be accessible elsewhere within the crate, but not outside it, the
+ /// `pub(crate)` visibility is recommended to be used instead. This more clearly expresses the
+ /// intent that the item is only visible within its own crate.
///
/// This lint is "allow" by default because it will trigger for a large
/// amount existing Rust code, and has some false-positives. Eventually it
@@ -1447,8 +1461,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else {
return
};
- if let hir::TyKind::OpaqueDef(..) = ty.kind {
- // Bounds are respected for `type X = impl Trait`
+ if cx.tcx.type_of(item.owner_id.def_id).skip_binder().has_opaque_types() {
+ // Bounds are respected for `type X = impl Trait` and `type X = (impl Trait, Y);`
return;
}
if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() {
@@ -1574,34 +1588,25 @@ declare_lint_pass!(
impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
- use rustc_middle::ty::Clause;
- use rustc_middle::ty::PredicateKind::*;
+ use rustc_middle::ty::ClauseKind;
if cx.tcx.features().trivial_bounds {
let predicates = cx.tcx.predicates_of(item.owner_id);
for &(predicate, span) in predicates.predicates {
let predicate_kind_name = match predicate.kind().skip_binder() {
- Clause(Clause::Trait(..)) => "trait",
- Clause(Clause::TypeOutlives(..)) |
- Clause(Clause::RegionOutlives(..)) => "lifetime",
+ ClauseKind::Trait(..) => "trait",
+ ClauseKind::TypeOutlives(..) |
+ ClauseKind::RegionOutlives(..) => "lifetime",
// `ConstArgHasType` is never global as `ct` is always a param
- Clause(Clause::ConstArgHasType(..)) |
+ ClauseKind::ConstArgHasType(..)
// Ignore projections, as they can only be global
// if the trait bound is global
- Clause(Clause::Projection(..)) |
- AliasRelate(..) |
+ | ClauseKind::Projection(..)
// Ignore bounds that a user can't type
- WellFormed(..) |
- ObjectSafe(..) |
- ClosureKind(..) |
- Subtype(..) |
- Coerce(..) |
+ | ClauseKind::WellFormed(..)
// FIXME(generic_const_exprs): `ConstEvaluatable` can be written
- ConstEvaluatable(..) |
- ConstEquate(..) |
- Ambiguous |
- TypeWellFormedFromEnv(..) => continue,
+ | ClauseKind::ConstEvaluatable(..) => continue,
};
if predicate.is_global() {
cx.emit_spanned_lint(
@@ -1971,8 +1976,8 @@ impl ExplicitOutlivesRequirements {
) -> Vec<ty::Region<'tcx>> {
inferred_outlives
.iter()
- .filter_map(|(clause, _)| match *clause {
- ty::Clause::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
+ .filter_map(|(clause, _)| match clause.kind().skip_binder() {
+ ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b),
_ => None,
},
@@ -1987,8 +1992,8 @@ impl ExplicitOutlivesRequirements {
) -> Vec<ty::Region<'tcx>> {
inferred_outlives
.iter()
- .filter_map(|(clause, _)| match *clause {
- ty::Clause::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
+ .filter_map(|(clause, _)| match clause.kind().skip_binder() {
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
a.is_param(index).then_some(b)
}
_ => None,
@@ -2106,12 +2111,16 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
}
let ty_generics = cx.tcx.generics_of(def_id);
+ let num_where_predicates = hir_generics
+ .predicates
+ .iter()
+ .filter(|predicate| predicate.in_where_clause())
+ .count();
let mut bound_count = 0;
let mut lint_spans = Vec::new();
let mut where_lint_spans = Vec::new();
- let mut dropped_predicate_count = 0;
- let num_predicates = hir_generics.predicates.len();
+ let mut dropped_where_predicate_count = 0;
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
let (relevant_lifetimes, bounds, predicate_span, in_where_clause) =
match where_predicate {
@@ -2168,8 +2177,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
bound_count += bound_spans.len();
let drop_predicate = bound_spans.len() == bounds.len();
- if drop_predicate {
- dropped_predicate_count += 1;
+ if drop_predicate && in_where_clause {
+ dropped_where_predicate_count += 1;
}
if drop_predicate {
@@ -2178,7 +2187,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
} else if predicate_span.from_expansion() {
// Don't try to extend the span if it comes from a macro expansion.
where_lint_spans.push(predicate_span);
- } else if i + 1 < num_predicates {
+ } else if i + 1 < num_where_predicates {
// If all the bounds on a predicate were inferable and there are
// further predicates, we want to eat the trailing comma.
let next_predicate_span = hir_generics.predicates[i + 1].span();
@@ -2206,9 +2215,10 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
}
}
- // If all predicates are inferable, drop the entire clause
+ // If all predicates in where clause are inferable, drop the entire clause
// (including the `where`)
- if hir_generics.has_where_clause_predicates && dropped_predicate_count == num_predicates
+ if hir_generics.has_where_clause_predicates
+ && dropped_where_predicate_count == num_where_predicates
{
let where_span = hir_generics.where_clause_span;
// Extend the where clause back to the closing `>` of the
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 1d0c43e95..3761754f3 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -952,6 +952,10 @@ pub trait LintContext: Sized {
db.span_label(first_reexport_span, format!("the name `{}` in the {} namespace is first re-exported here", name, namespace));
db.span_label(duplicate_reexport_span, format!("but the name `{}` in the {} namespace is also re-exported here", name, namespace));
}
+ BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => {
+ db.span_note(glob_reexport_span, format!("the name `{}` in the {} namespace is supposed to be publicly re-exported here", name, namespace));
+ db.span_note(private_item_span, "but the private item here shadows it".to_owned());
+ }
}
// Rewrap `db`, and pass control to the user.
decorate(db)
@@ -1341,7 +1345,7 @@ impl<'tcx> LateContext<'tcx> {
tcx.associated_items(trait_id)
.find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
.and_then(|assoc| {
- let proj = tcx.mk_projection(assoc.def_id, [self_ty]);
+ let proj = Ty::new_projection(tcx, assoc.def_id, [self_ty]);
tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
})
}
diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs
index 77e4a7669..467f53d44 100644
--- a/compiler/rustc_lint/src/drop_forget_useless.rs
+++ b/compiler/rustc_lint/src/drop_forget_useless.rs
@@ -1,8 +1,12 @@
use rustc_hir::{Arm, Expr, ExprKind, Node};
+use rustc_middle::ty;
use rustc_span::sym;
use crate::{
- lints::{DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag},
+ lints::{
+ DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag, UndroppedManuallyDropsDiag,
+ UndroppedManuallyDropsSuggestion,
+ },
LateContext, LateLintPass, LintContext,
};
@@ -109,7 +113,29 @@ declare_lint! {
"calls to `std::mem::forget` with a value that implements Copy"
}
-declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]);
+declare_lint! {
+ /// The `undropped_manually_drops` lint check for calls to `std::mem::drop` with
+ /// a value of `std::mem::ManuallyDrop` which doesn't drop.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// struct S;
+ /// drop(std::mem::ManuallyDrop::new(S));
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// `ManuallyDrop` does not drop it's inner value so calling `std::mem::drop` will
+ /// not drop the inner value of the `ManuallyDrop` either.
+ pub UNDROPPED_MANUALLY_DROPS,
+ Deny,
+ "calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value"
+}
+
+declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES, UNDROPPED_MANUALLY_DROPS]);
impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@@ -134,6 +160,20 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
sym::mem_forget if is_copy => {
cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span });
}
+ sym::mem_drop if let ty::Adt(adt, _) = arg_ty.kind() && adt.is_manually_drop() => {
+ cx.emit_spanned_lint(
+ UNDROPPED_MANUALLY_DROPS,
+ expr.span,
+ UndroppedManuallyDropsDiag {
+ arg_ty,
+ label: arg.span,
+ suggestion: UndroppedManuallyDropsSuggestion {
+ start_span: arg.span.shrink_to_lo(),
+ end_span: arg.span.shrink_to_hi()
+ }
+ }
+ );
+ }
_ => return,
};
}
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index bbae3d368..68167487a 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -39,7 +39,7 @@ impl AddToDiagnostic for OverruledAttributeSub {
diag.span_label(span, fluent::lint_node_source);
if let Some(rationale) = reason {
#[allow(rustc::untranslatable_diagnostic)]
- diag.note(rationale.as_str());
+ diag.note(rationale.to_string());
}
}
OverruledAttributeSub::CommandLineSource => {
diff --git a/compiler/rustc_lint/src/invalid_from_utf8.rs b/compiler/rustc_lint/src/invalid_from_utf8.rs
new file mode 100644
index 000000000..3291286ad
--- /dev/null
+++ b/compiler/rustc_lint/src/invalid_from_utf8.rs
@@ -0,0 +1,118 @@
+use std::str::Utf8Error;
+
+use rustc_ast::{BorrowKind, LitKind};
+use rustc_hir::{Expr, ExprKind};
+use rustc_span::source_map::Spanned;
+use rustc_span::sym;
+
+use crate::lints::InvalidFromUtf8Diag;
+use crate::{LateContext, LateLintPass, LintContext};
+
+declare_lint! {
+ /// The `invalid_from_utf8_unchecked` lint checks for calls to
+ /// `std::str::from_utf8_unchecked` and `std::str::from_utf8_unchecked_mut`
+ /// with an invalid UTF-8 literal.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// # #[allow(unused)]
+ /// unsafe {
+ /// std::str::from_utf8_unchecked(b"Ru\x82st");
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Creating such a `str` would result in undefined behavior as per documentation
+ /// for `std::str::from_utf8_unchecked` and `std::str::from_utf8_unchecked_mut`.
+ pub INVALID_FROM_UTF8_UNCHECKED,
+ Deny,
+ "using a non UTF-8 literal in `std::str::from_utf8_unchecked`"
+}
+
+declare_lint! {
+ /// The `invalid_from_utf8` lint checks for calls to
+ /// `std::str::from_utf8` and `std::str::from_utf8_mut`
+ /// with an invalid UTF-8 literal.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # #[allow(unused)]
+ /// std::str::from_utf8(b"Ru\x82st");
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Trying to create such a `str` would always return an error as per documentation
+ /// for `std::str::from_utf8` and `std::str::from_utf8_mut`.
+ pub INVALID_FROM_UTF8,
+ Warn,
+ "using a non UTF-8 literal in `std::str::from_utf8`"
+}
+
+declare_lint_pass!(InvalidFromUtf8 => [INVALID_FROM_UTF8_UNCHECKED, INVALID_FROM_UTF8]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if let ExprKind::Call(path, [arg]) = expr.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
+ && [sym::str_from_utf8, sym::str_from_utf8_mut,
+ sym::str_from_utf8_unchecked, sym::str_from_utf8_unchecked_mut].contains(&diag_item)
+ {
+ let lint = |utf8_error: Utf8Error| {
+ let label = arg.span;
+ let method = diag_item.as_str().strip_prefix("str_").unwrap();
+ let method = format!("std::str::{method}");
+ let valid_up_to = utf8_error.valid_up_to();
+ let is_unchecked_variant = diag_item.as_str().contains("unchecked");
+
+ cx.emit_spanned_lint(
+ if is_unchecked_variant { INVALID_FROM_UTF8_UNCHECKED } else { INVALID_FROM_UTF8 },
+ expr.span,
+ if is_unchecked_variant {
+ InvalidFromUtf8Diag::Unchecked { method, valid_up_to, label }
+ } else {
+ InvalidFromUtf8Diag::Checked { method, valid_up_to, label }
+ }
+ )
+ };
+
+ match &arg.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => {
+ if let LitKind::ByteStr(bytes, _) = &lit
+ && let Err(utf8_error) = std::str::from_utf8(bytes)
+ {
+ lint(utf8_error);
+ }
+ },
+ ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
+ let elements = args.iter().map(|e|{
+ match &e.kind {
+ ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
+ LitKind::Byte(b) => Some(*b),
+ LitKind::Int(b, _) => Some(*b as u8),
+ _ => None
+ }
+ _ => None
+ }
+ }).collect::<Option<Vec<_>>>();
+
+ if let Some(elements) = elements
+ && let Err(utf8_error) = std::str::from_utf8(&elements)
+ {
+ lint(utf8_error);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 8a4a451f8..fb12ded71 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -16,6 +16,7 @@
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
use rustc_ast as ast;
+use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::{join, DynSend};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
@@ -157,10 +158,12 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
}
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
- self.with_lint_attrs(e.hir_id, |cx| {
- lint_callback!(cx, check_expr, e);
- hir_visit::walk_expr(cx, e);
- lint_callback!(cx, check_expr_post, e);
+ ensure_sufficient_stack(|| {
+ self.with_lint_attrs(e.hir_id, |cx| {
+ lint_callback!(cx, check_expr, e);
+ hir_visit::walk_expr(cx, e);
+ lint_callback!(cx, check_expr_post, e);
+ })
})
}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index dfddfe09a..602071a55 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -60,6 +60,7 @@ mod expect;
mod for_loops_over_fallibles;
pub mod hidden_unicode_codepoints;
mod internal;
+mod invalid_from_utf8;
mod late;
mod let_underscore;
mod levels;
@@ -75,6 +76,7 @@ mod opaque_hidden_inferred_bound;
mod pass_by_value;
mod passes;
mod redundant_semicolon;
+mod reference_casting;
mod traits;
mod types;
mod unused;
@@ -102,6 +104,7 @@ use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use hidden_unicode_codepoints::*;
use internal::*;
+use invalid_from_utf8::*;
use let_underscore::*;
use map_unit_fn::*;
use methods::*;
@@ -113,6 +116,7 @@ use noop_method_call::*;
use opaque_hidden_inferred_bound::*;
use pass_by_value::*;
use redundant_semicolon::*;
+use reference_casting::*;
use traits::*;
use types::*;
use unused::*;
@@ -207,10 +211,12 @@ late_lint_methods!(
HardwiredLints: HardwiredLints,
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
+ InvalidFromUtf8: InvalidFromUtf8,
VariantSizeDifferences: VariantSizeDifferences,
BoxPointers: BoxPointers,
PathStatements: PathStatements,
LetUnderscore: LetUnderscore,
+ InvalidReferenceCasting: InvalidReferenceCasting,
// Depends on referenced function signatures in expressions
UnusedResults: UnusedResults,
NonUpperCaseGlobals: NonUpperCaseGlobals,
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index d96723a68..9260237fb 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -10,7 +10,7 @@ use rustc_errors::{
use rustc_hir::def_id::DefId;
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::{
- inhabitedness::InhabitedPredicate, PolyExistentialTraitRef, Predicate, Ty, TyCtxt,
+ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt,
};
use rustc_session::parse::ParseSess;
use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol};
@@ -352,7 +352,7 @@ impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
#[diag(lint_builtin_trivial_bounds)]
pub struct BuiltinTrivialBounds<'a> {
pub predicate_kind_name: &'a str,
- pub predicate: Predicate<'a>,
+ pub predicate: Clause<'a>,
}
#[derive(LintDiagnostic)]
@@ -699,6 +699,49 @@ pub struct ForgetCopyDiag<'a> {
pub label: Span,
}
+#[derive(LintDiagnostic)]
+#[diag(lint_undropped_manually_drops)]
+pub struct UndroppedManuallyDropsDiag<'a> {
+ pub arg_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+ #[subdiagnostic]
+ pub suggestion: UndroppedManuallyDropsSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
+pub struct UndroppedManuallyDropsSuggestion {
+ #[suggestion_part(code = "std::mem::ManuallyDrop::into_inner(")]
+ pub start_span: Span,
+ #[suggestion_part(code = ")")]
+ pub end_span: Span,
+}
+
+// invalid_from_utf8.rs
+#[derive(LintDiagnostic)]
+pub enum InvalidFromUtf8Diag {
+ #[diag(lint_invalid_from_utf8_unchecked)]
+ Unchecked {
+ method: String,
+ valid_up_to: usize,
+ #[label]
+ label: Span,
+ },
+ #[diag(lint_invalid_from_utf8_checked)]
+ Checked {
+ method: String,
+ valid_up_to: usize,
+ #[label]
+ label: Span,
+ },
+}
+
+// reference_casting.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_invalid_reference_casting)]
+pub struct InvalidReferenceCastingDiag;
+
// hidden_unicode_codepoints.rs
#[derive(LintDiagnostic)]
#[diag(lint_hidden_unicode_codepoints)]
@@ -1219,7 +1262,7 @@ pub struct RedundantSemicolonsDiag {
// traits.rs
pub struct DropTraitConstraintsDiag<'a> {
- pub predicate: Predicate<'a>,
+ pub predicate: Clause<'a>,
pub tcx: TyCtxt<'a>,
pub def_id: DefId,
}
@@ -1303,6 +1346,8 @@ pub struct OverflowingBinHex<'a> {
pub sign: OverflowingBinHexSign,
#[subdiagnostic]
pub sub: Option<OverflowingBinHexSub<'a>>,
+ #[subdiagnostic]
+ pub sign_bit_sub: Option<OverflowingBinHexSignBitSub<'a>>,
}
pub enum OverflowingBinHexSign {
@@ -1347,6 +1392,21 @@ pub enum OverflowingBinHexSub<'a> {
Help { suggestion_ty: &'a str },
}
+#[derive(Subdiagnostic)]
+#[suggestion(
+ lint_sign_bit_suggestion,
+ code = "{lit_no_suffix}{uint_ty} as {int_ty}",
+ applicability = "maybe-incorrect"
+)]
+pub struct OverflowingBinHexSignBitSub<'a> {
+ #[primary_span]
+ pub span: Span,
+ pub lit_no_suffix: &'a str,
+ pub negative_val: String,
+ pub uint_ty: &'a str,
+ pub int_ty: &'a str,
+}
+
#[derive(LintDiagnostic)]
#[diag(lint_overflowing_int)]
#[note]
@@ -1395,6 +1455,36 @@ pub struct OverflowingLiteral<'a> {
#[diag(lint_unused_comparisons)]
pub struct UnusedComparisons;
+#[derive(LintDiagnostic)]
+pub enum InvalidNanComparisons {
+ #[diag(lint_invalid_nan_comparisons_eq_ne)]
+ EqNe {
+ #[subdiagnostic]
+ suggestion: InvalidNanComparisonsSuggestion,
+ },
+ #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)]
+ LtLeGtGe,
+}
+
+#[derive(Subdiagnostic)]
+pub enum InvalidNanComparisonsSuggestion {
+ #[multipart_suggestion(
+ lint_suggestion,
+ style = "verbose",
+ applicability = "machine-applicable"
+ )]
+ Spanful {
+ #[suggestion_part(code = "!")]
+ neg: Option<Span>,
+ #[suggestion_part(code = ".is_nan()")]
+ float: Span,
+ #[suggestion_part(code = "")]
+ nan_plus_binop: Span,
+ },
+ #[help(lint_suggestion)]
+ Spanless,
+}
+
pub struct ImproperCTypes<'a> {
pub ty: Ty<'a>,
pub desc: &'a str,
@@ -1465,8 +1555,29 @@ pub struct UnusedOp<'a> {
pub op: &'a str,
#[label]
pub label: Span,
- #[suggestion(style = "verbose", code = "let _ = ", applicability = "maybe-incorrect")]
- pub suggestion: Span,
+ #[subdiagnostic]
+ pub suggestion: UnusedOpSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+pub enum UnusedOpSuggestion {
+ #[suggestion(
+ lint_suggestion,
+ style = "verbose",
+ code = "let _ = ",
+ applicability = "maybe-incorrect"
+ )]
+ NormalExpr {
+ #[primary_span]
+ span: Span,
+ },
+ #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")]
+ BlockTailExpr {
+ #[suggestion_part(code = "let _ = ")]
+ before_span: Span,
+ #[suggestion_part(code = ";")]
+ after_span: Span,
+ },
}
#[derive(LintDiagnostic)]
@@ -1509,15 +1620,25 @@ pub struct UnusedDef<'a, 'b> {
}
#[derive(Subdiagnostic)]
-#[suggestion(
- lint_suggestion,
- style = "verbose",
- code = "let _ = ",
- applicability = "maybe-incorrect"
-)]
-pub struct UnusedDefSuggestion {
- #[primary_span]
- pub span: Span,
+
+pub enum UnusedDefSuggestion {
+ #[suggestion(
+ lint_suggestion,
+ style = "verbose",
+ code = "let _ = ",
+ applicability = "maybe-incorrect"
+ )]
+ NormalExpr {
+ #[primary_span]
+ span: Span,
+ },
+ #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")]
+ BlockTailExpr {
+ #[suggestion_part(code = "let _ = ")]
+ before_span: Span,
+ #[suggestion_part(code = ";")]
+ after_span: Span,
+ },
}
// Needed because of def_path_str
@@ -1531,7 +1652,7 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id));
// check for #[must_use = "..."]
if let Some(note) = self.note {
- diag.note(note.as_str());
+ diag.note(note.to_string());
}
if let Some(sugg) = self.suggestion {
diag.subdiagnostic(sugg);
diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
index c2ed0e19f..53fe0ceb2 100644
--- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
+++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
@@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
.super_predicates_of(def_id)
.predicates
.into_iter()
- .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred());
+ .filter_map(|(pred, _)| pred.as_trait_clause());
if direct_super_traits_iter.count() > 1 {
cx.emit_spanned_lint(
MULTIPLE_SUPERTRAIT_UPCASTABLE,
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 79253cbc8..145de4948 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -533,6 +533,10 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) {
if let GenericParamKind::Const { .. } = param.kind {
+ // `rustc_host` params are explicitly allowed to be lowercase.
+ if cx.tcx.has_attr(param.def_id, sym::rustc_host) {
+ return;
+ }
NonUpperCaseGlobals::check_upper_case(cx, "const parameter", &param.name.ident());
}
}
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 15715c8fc..09a1651c2 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// Liberate bound regions in the predicate since we
// don't actually care about lifetimes in this check.
let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
- let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = predicate else {
+ let ty::ClauseKind::Projection(proj) = predicate else {
continue;
};
// Only check types, since those are the only things that may
@@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
}
let proj_ty =
- cx.tcx.mk_projection(proj.projection_ty.def_id, proj.projection_ty.substs);
+ Ty::new_projection(cx.tcx, proj.projection_ty.def_id, proj.projection_ty.substs);
// For every instance of the projection type in the bounds,
// replace them with the term we're assigning to the associated
// type in our opaque type.
@@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
(
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }),
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)),
+ ty::ClauseKind::Trait(trait_pred),
) => Some(AddBound {
suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
trait_ref: trait_pred.print_modifiers_and_trait_path(),
@@ -144,7 +144,8 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
OPAQUE_HIDDEN_INFERRED_BOUND,
pred_span,
OpaqueHiddenInferredBoundLint {
- ty: cx.tcx.mk_opaque(
+ ty: Ty::new_opaque(
+ cx.tcx,
def_id,
ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
),
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
new file mode 100644
index 000000000..db8d7302a
--- /dev/null
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -0,0 +1,73 @@
+use rustc_ast::Mutability;
+use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp};
+use rustc_middle::ty;
+use rustc_span::sym;
+
+use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext};
+
+declare_lint! {
+ /// The `invalid_reference_casting` lint checks for casts of `&T` to `&mut T`
+ /// without using interior mutability.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// # #![deny(invalid_reference_casting)]
+ /// fn x(r: &i32) {
+ /// unsafe {
+ /// *(r as *const i32 as *mut i32) += 1;
+ /// }
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Casting `&T` to `&mut T` without using interior mutability is undefined behavior,
+ /// as it's a violation of Rust reference aliasing requirements.
+ ///
+ /// `UnsafeCell` is the only way to obtain aliasable data that is considered
+ /// mutable.
+ INVALID_REFERENCE_CASTING,
+ Allow,
+ "casts of `&T` to `&mut T` without interior mutability"
+}
+
+declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; };
+
+ let e = e.peel_blocks();
+ let e = if let ExprKind::Cast(e, t) = e.kind
+ && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind {
+ e
+ } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
+ expr
+ } else {
+ return;
+ };
+
+ let e = e.peel_blocks();
+ let e = if let ExprKind::Cast(e, t) = e.kind
+ && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind {
+ e
+ } else if let ExprKind::Call(path, [arg]) = e.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
+ arg
+ } else {
+ return;
+ };
+
+ let e = e.peel_blocks();
+ if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() {
+ cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag);
+ }
+ }
+}
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 7ea1a138b..de1120806 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -87,12 +87,11 @@ declare_lint_pass!(
impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
- use rustc_middle::ty::Clause;
- use rustc_middle::ty::PredicateKind::*;
+ use rustc_middle::ty::ClauseKind;
let predicates = cx.tcx.explicit_predicates_of(item.owner_id);
for &(predicate, span) in predicates.predicates {
- let Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() else {
+ let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else {
continue
};
let def_id = trait_predicate.trait_ref.def_id;
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 4bf4fda82..ed4fb9860 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -2,7 +2,8 @@ use crate::{
fluent_generated as fluent,
lints::{
AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
- InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
+ InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
+ OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
VariantSizeDifferencesDiag,
@@ -113,13 +114,35 @@ declare_lint! {
"detects enums with widely varying variant sizes"
}
+declare_lint! {
+ /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
+ /// as one of the operand.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let a = 2.3f32;
+ /// if a == f32::NAN {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// NaN does not compare meaningfully to anything – not
+ /// even itself – so those comparisons are always false.
+ INVALID_NAN_COMPARISONS,
+ Warn,
+ "detects invalid floating point NaN comparisons"
+}
+
#[derive(Copy, Clone)]
pub struct TypeLimits {
/// Id of the last visited negated expression
negated_expr_id: Option<hir::HirId>,
}
-impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]);
+impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
impl TypeLimits {
pub fn new() -> TypeLimits {
@@ -275,10 +298,50 @@ fn report_bin_hex_error(
}
},
);
+ let sign_bit_sub = (!negative)
+ .then(|| {
+ let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else {
+ return None;
+ };
+
+ let Some(bit_width) = int_ty.bit_width() else {
+ return None; // isize case
+ };
+
+ // Skip if sign bit is not set
+ if (val & (1 << (bit_width - 1))) == 0 {
+ return None;
+ }
+
+ let lit_no_suffix =
+ if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+ repr_str.split_at(pos).0
+ } else {
+ &repr_str
+ };
+
+ Some(OverflowingBinHexSignBitSub {
+ span: expr.span,
+ lit_no_suffix,
+ negative_val: actually.clone(),
+ int_ty: int_ty.name_str(),
+ uint_ty: int_ty.to_unsigned().name_str(),
+ })
+ })
+ .flatten();
+
cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
expr.span,
- OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
+ OverflowingBinHex {
+ ty: t,
+ lit: repr_str.clone(),
+ dec: val,
+ actually,
+ sign,
+ sub,
+ sign_bit_sub,
+ },
)
}
@@ -486,6 +549,68 @@ fn lint_literal<'tcx>(
}
}
+fn lint_nan<'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx hir::Expr<'tcx>,
+ binop: hir::BinOp,
+ l: &'tcx hir::Expr<'tcx>,
+ r: &'tcx hir::Expr<'tcx>,
+) {
+ fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+ let expr = expr.peel_blocks().peel_borrows();
+ match expr.kind {
+ ExprKind::Path(qpath) => {
+ let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; };
+
+ matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan))
+ }
+ _ => false,
+ }
+ }
+
+ fn eq_ne(
+ e: &hir::Expr<'_>,
+ l: &hir::Expr<'_>,
+ r: &hir::Expr<'_>,
+ f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
+ ) -> InvalidNanComparisons {
+ let suggestion =
+ if let Some(l_span) = l.span.find_ancestor_inside(e.span) &&
+ let Some(r_span) = r.span.find_ancestor_inside(e.span) {
+ f(l_span, r_span)
+ } else {
+ InvalidNanComparisonsSuggestion::Spanless
+ };
+
+ InvalidNanComparisons::EqNe { suggestion }
+ }
+
+ let lint = match binop.node {
+ hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
+ eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
+ nan_plus_binop: l_span.until(r_span),
+ float: r_span.shrink_to_hi(),
+ neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
+ })
+ }
+ hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
+ eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
+ nan_plus_binop: l_span.shrink_to_hi().to(r_span),
+ float: l_span.shrink_to_hi(),
+ neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
+ })
+ }
+ hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
+ if is_nan(cx, l) || is_nan(cx, r) =>
+ {
+ InvalidNanComparisons::LtLeGtGe
+ }
+ _ => return,
+ };
+
+ cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
+}
+
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
match e.kind {
@@ -496,8 +621,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
}
}
hir::ExprKind::Binary(binop, ref l, ref r) => {
- if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
- cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
+ if is_comparison(binop) {
+ if !check_limits(cx, binop, &l, &r) {
+ cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
+ } else {
+ lint_nan(cx, e, binop, l, r);
+ }
}
}
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
@@ -733,12 +862,12 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
};
return get_nullable_type(cx, inner_field_ty);
}
- ty::Int(ty) => tcx.mk_mach_int(ty),
- ty::Uint(ty) => tcx.mk_mach_uint(ty),
- ty::RawPtr(ty_mut) => tcx.mk_ptr(ty_mut),
+ ty::Int(ty) => Ty::new_int(tcx, ty),
+ ty::Uint(ty) => Ty::new_uint(tcx, ty),
+ ty::RawPtr(ty_mut) => Ty::new_ptr(tcx, ty_mut),
// As these types are always non-null, the nullable equivalent of
// Option<T> of these types are their raw pointer counterparts.
- ty::Ref(_region, ty, mutbl) => tcx.mk_ptr(ty::TypeAndMut { ty, mutbl }),
+ ty::Ref(_region, ty, mutbl) => Ty::new_ptr(tcx, ty::TypeAndMut { ty, mutbl }),
ty::FnPtr(..) => {
// There is no nullable equivalent for Rust's function pointers -- you
// must use an Option<fn(..) -> _> to represent it.
@@ -834,12 +963,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
substs: SubstsRef<'tcx>,
) -> FfiResult<'tcx> {
let field_ty = field.ty(self.cx.tcx, substs);
- if field_ty.has_opaque_types() {
- self.check_type_for_ffi(cache, field_ty)
- } else {
- let field_ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, field_ty);
- self.check_type_for_ffi(cache, field_ty)
- }
+ let field_ty = self
+ .cx
+ .tcx
+ .try_normalize_erasing_regions(self.cx.param_env, field_ty)
+ .unwrap_or(field_ty);
+ self.check_type_for_ffi(cache, field_ty)
}
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
@@ -853,39 +982,43 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
) -> FfiResult<'tcx> {
use FfiResult::*;
- let transparent_safety = def.repr().transparent().then(|| {
- // Can assume that at most one field is not a ZST, so only check
- // that field's type for FFI-safety.
+ let transparent_with_all_zst_fields = if def.repr().transparent() {
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
- return self.check_field_type_for_ffi(cache, field, substs);
+ // Transparent newtypes have at most one non-ZST field which needs to be checked..
+ match self.check_field_type_for_ffi(cache, field, substs) {
+ FfiUnsafe { ty, .. } if ty.is_unit() => (),
+ r => return r,
+ }
+
+ false
} else {
- // All fields are ZSTs; this means that the type should behave
- // like (), which is FFI-unsafe... except if all fields are PhantomData,
- // which is tested for below
- FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
+ // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
+ // `PhantomData`).
+ true
}
- });
- // We can't completely trust repr(C) markings; make sure the fields are
- // actually safe.
+ } else {
+ false
+ };
+
+ // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
let mut all_phantom = !variant.fields.is_empty();
for field in &variant.fields {
- match self.check_field_type_for_ffi(cache, &field, substs) {
- FfiSafe => {
- all_phantom = false;
- }
- FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
- return FfiUnsafe {
- ty,
- reason: fluent::lint_improper_ctypes_enum_phantomdata,
- help: None,
- };
- }
- FfiPhantom(..) => {}
- r => return transparent_safety.unwrap_or(r),
+ all_phantom &= match self.check_field_type_for_ffi(cache, &field, substs) {
+ FfiSafe => false,
+ // `()` fields are FFI-safe!
+ FfiUnsafe { ty, .. } if ty.is_unit() => false,
+ FfiPhantom(..) => true,
+ r @ FfiUnsafe { .. } => return r,
}
}
- if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
+ if all_phantom {
+ FfiPhantom(ty)
+ } else if transparent_with_all_zst_fields {
+ FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
+ } else {
+ FfiSafe
+ }
}
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
@@ -1088,25 +1221,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
let sig = tcx.erase_late_bound_regions(sig);
- if !sig.output().is_unit() {
- let r = self.check_type_for_ffi(cache, sig.output());
- match r {
- FfiSafe => {}
- _ => {
- return r;
- }
- }
- }
for arg in sig.inputs() {
- let r = self.check_type_for_ffi(cache, *arg);
- match r {
+ match self.check_type_for_ffi(cache, *arg) {
FfiSafe => {}
- _ => {
- return r;
- }
+ r => return r,
}
}
- FfiSafe
+
+ let ret_ty = sig.output();
+ if ret_ty.is_unit() {
+ return FfiSafe;
+ }
+
+ self.check_type_for_ffi(cache, ret_ty)
}
ty::Foreign(..) => FfiSafe,
@@ -1126,7 +1253,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
ty::Param(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
| ty::Infer(..)
| ty::Bound(..)
| ty::Error(_)
@@ -1188,7 +1315,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if let Some(ty) = self
.cx
.tcx
- .normalize_erasing_regions(self.cx.param_env, ty)
+ .try_normalize_erasing_regions(self.cx.param_env, ty)
+ .unwrap_or(ty)
.visit_with(&mut ProhibitOpaqueTypes)
.break_value()
{
@@ -1206,16 +1334,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
is_static: bool,
is_return_type: bool,
) {
- // We have to check for opaque types before `normalize_erasing_regions`,
- // which will replace opaque types with their underlying concrete type.
if self.check_for_opaque_ty(sp, ty) {
// We've already emitted an error due to an opaque type.
return;
}
- // it is only OK to use this function because extern fns cannot have
- // any generic types right now:
- let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty);
+ let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.param_env, ty).unwrap_or(ty);
// C doesn't really support passing arrays by value - the only way to pass an array by value
// is through a struct. So, first test that the top level isn't an array, and then
@@ -1225,7 +1349,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
// Don't report FFI errors for unit return types. This check exists here, and not in
- // `check_foreign_fn` (where it would make more sense) so that normalization has definitely
+ // the caller (where it would make more sense) so that normalization has definitely
// happened.
if is_return_type && ty.is_unit() {
return;
@@ -1241,16 +1365,35 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
None,
);
}
- // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
- // argument, which after substitution, is `()`, then this branch can be hit.
- FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
FfiResult::FfiUnsafe { ty, reason, help } => {
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
}
}
}
- fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+ /// Check if a function's argument types and result type are "ffi-safe".
+ ///
+ /// For a external ABI function, argument types and the result type are walked to find fn-ptr
+ /// types that have external ABIs, as these still need checked.
+ fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
+ let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
+ let sig = self.cx.tcx.erase_late_bound_regions(sig);
+
+ for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
+ for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
+ self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
+ }
+ }
+
+ if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
+ for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
+ self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
+ }
+ }
+ }
+
+ /// Check if a function's argument types and result type are "ffi-safe".
+ fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
let sig = self.cx.tcx.erase_late_bound_regions(sig);
@@ -1259,8 +1402,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
- let ret_ty = sig.output();
- self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true);
+ self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
}
}
@@ -1275,28 +1417,131 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
)
}
+
+ /// Find any fn-ptr types with external ABIs in `ty`.
+ ///
+ /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
+ fn find_fn_ptr_ty_with_external_abi(
+ &self,
+ hir_ty: &hir::Ty<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Vec<(Ty<'tcx>, Span)> {
+ struct FnPtrFinder<'parent, 'a, 'tcx> {
+ visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
+ spans: Vec<Span>,
+ tys: Vec<Ty<'tcx>>,
+ }
+
+ impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
+ fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
+ debug!(?ty);
+ if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
+ && !self.visitor.is_internal_abi(*abi)
+ {
+ self.spans.push(ty.span);
+ }
+
+ hir::intravisit::walk_ty(self, ty)
+ }
+ }
+
+ impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
+ type BreakTy = Ty<'tcx>;
+
+ fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
+ self.tys.push(ty);
+ }
+
+ ty.super_visit_with(self)
+ }
+ }
+
+ let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
+ ty.visit_with(&mut visitor);
+ hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
+
+ iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
+ }
}
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
- fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) {
+ fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
- if !vis.is_internal_abi(abi) {
- match it.kind {
- hir::ForeignItemKind::Fn(ref decl, _, _) => {
- vis.check_foreign_fn(it.owner_id.def_id, decl);
- }
- hir::ForeignItemKind::Static(ref ty, _) => {
- vis.check_foreign_static(it.owner_id, ty.span);
- }
- hir::ForeignItemKind::Type => (),
+ match it.kind {
+ hir::ForeignItemKind::Fn(ref decl, _, _) if !vis.is_internal_abi(abi) => {
+ vis.check_foreign_fn(it.owner_id.def_id, decl);
}
+ hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
+ vis.check_foreign_static(it.owner_id, ty.span);
+ }
+ hir::ForeignItemKind::Fn(ref decl, _, _) => vis.check_fn(it.owner_id.def_id, decl),
+ hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
+ }
+ }
+}
+
+impl ImproperCTypesDefinitions {
+ fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ hir_ty: &'tcx hir::Ty<'_>,
+ ty: Ty<'tcx>,
+ ) {
+ let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
+ for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
+ vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
}
}
}
+/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
+/// `extern "C" { }` blocks):
+///
+/// - `extern "<abi>" fn` definitions are checked in the same way as the
+/// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
+/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
+/// checked for extern fn-ptrs with external ABIs.
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ match item.kind {
+ hir::ItemKind::Static(ty, ..)
+ | hir::ItemKind::Const(ty, ..)
+ | hir::ItemKind::TyAlias(ty, ..) => {
+ self.check_ty_maybe_containing_foreign_fnptr(
+ cx,
+ ty,
+ cx.tcx.type_of(item.owner_id).subst_identity(),
+ );
+ }
+ // See `check_fn`..
+ hir::ItemKind::Fn(..) => {}
+ // See `check_field_def`..
+ hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {}
+ // Doesn't define something that can contain a external type to be checked.
+ hir::ItemKind::Impl(..)
+ | hir::ItemKind::TraitAlias(..)
+ | hir::ItemKind::Trait(..)
+ | hir::ItemKind::OpaqueTy(..)
+ | hir::ItemKind::GlobalAsm(..)
+ | hir::ItemKind::ForeignMod { .. }
+ | hir::ItemKind::Mod(..)
+ | hir::ItemKind::Macro(..)
+ | hir::ItemKind::Use(..)
+ | hir::ItemKind::ExternCrate(..) => {}
+ }
+ }
+
+ fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
+ self.check_ty_maybe_containing_foreign_fnptr(
+ cx,
+ field.ty,
+ cx.tcx.type_of(field.def_id).subst_identity(),
+ );
+ }
+
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
@@ -1315,7 +1560,9 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
};
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
- if !vis.is_internal_abi(abi) {
+ if vis.is_internal_abi(abi) {
+ vis.check_fn(id, decl);
+ } else {
vis.check_foreign_fn(id, decl);
}
}
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 8f75fa11d..5015b751e 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1,7 +1,8 @@
use crate::lints::{
PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDefSuggestion, UnusedDelim,
- UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
+ UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion,
+ UnusedResult,
};
use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
@@ -93,7 +94,15 @@ declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
impl<'tcx> LateLintPass<'tcx> for UnusedResults {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
- let hir::StmtKind::Semi(expr) = s.kind else { return; };
+ let hir::StmtKind::Semi(mut expr) = s.kind else { return; };
+
+ let mut expr_is_from_block = false;
+ while let hir::ExprKind::Block(blk, ..) = expr.kind
+ && let hir::Block { expr: Some(e), .. } = blk
+ {
+ expr = e;
+ expr_is_from_block = true;
+ }
if let hir::ExprKind::Ret(..) = expr.kind {
return;
@@ -113,6 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
expr.span,
"output of future returned by ",
"",
+ expr_is_from_block,
)
{
// We have a bare `foo().await;` on an opaque type from an async function that was
@@ -125,13 +135,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
let must_use_result = is_ty_must_use(cx, ty, &expr, expr.span);
let type_lint_emitted_or_suppressed = match must_use_result {
Some(path) => {
- emit_must_use_untranslated(cx, &path, "", "", 1, false);
+ emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
true
}
None => false,
};
- let fn_warned = check_fn_must_use(cx, expr);
+ let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
if !fn_warned && type_lint_emitted_or_suppressed {
// We don't warn about unused unit or uninhabited types.
@@ -176,7 +186,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
UnusedOp {
op: must_use_op,
label: expr.span,
- suggestion: expr.span.shrink_to_lo(),
+ suggestion: if expr_is_from_block {
+ UnusedOpSuggestion::BlockTailExpr {
+ before_span: expr.span.shrink_to_lo(),
+ after_span: expr.span.shrink_to_hi(),
+ }
+ } else {
+ UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() }
+ },
},
);
op_warned = true;
@@ -186,7 +203,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
}
- fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+ fn check_fn_must_use(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ expr_is_from_block: bool,
+ ) -> bool {
let maybe_def_id = match expr.kind {
hir::ExprKind::Call(ref callee, _) => {
match callee.kind {
@@ -207,7 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
_ => None,
};
if let Some(def_id) = maybe_def_id {
- check_must_use_def(cx, def_id, expr.span, "return value of ", "")
+ check_must_use_def(
+ cx,
+ def_id,
+ expr.span,
+ "return value of ",
+ "",
+ expr_is_from_block,
+ )
} else {
false
}
@@ -261,9 +289,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
.filter_only_self()
.find_map(|(pred, _span)| {
// We only look at the `DefId`, so it is safe to skip the binder here.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(
- ref poly_trait_predicate,
- )) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
+ pred.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
@@ -350,6 +377,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
span: Span,
descr_pre_path: &str,
descr_post_path: &str,
+ expr_is_from_block: bool,
) -> bool {
is_def_must_use(cx, def_id, span)
.map(|must_use_path| {
@@ -360,6 +388,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
descr_post_path,
1,
false,
+ expr_is_from_block,
)
})
.is_some()
@@ -373,6 +402,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
descr_post: &str,
plural_len: usize,
is_inner: bool,
+ expr_is_from_block: bool,
) {
let plural_suffix = pluralize!(plural_len);
@@ -380,21 +410,51 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
MustUsePath::Suppressed => {}
MustUsePath::Boxed(path) => {
let descr_pre = &format!("{}boxed ", descr_pre);
- emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true);
+ emit_must_use_untranslated(
+ cx,
+ path,
+ descr_pre,
+ descr_post,
+ plural_len,
+ true,
+ expr_is_from_block,
+ );
}
MustUsePath::Opaque(path) => {
let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix);
- emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true);
+ emit_must_use_untranslated(
+ cx,
+ path,
+ descr_pre,
+ descr_post,
+ plural_len,
+ true,
+ expr_is_from_block,
+ );
}
MustUsePath::TraitObject(path) => {
let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post);
- emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len, true);
+ emit_must_use_untranslated(
+ cx,
+ path,
+ descr_pre,
+ descr_post,
+ plural_len,
+ true,
+ expr_is_from_block,
+ );
}
MustUsePath::TupleElement(elems) => {
for (index, path) in elems {
let descr_post = &format!(" in tuple element {}", index);
emit_must_use_untranslated(
- cx, path, descr_pre, descr_post, plural_len, true,
+ cx,
+ path,
+ descr_pre,
+ descr_post,
+ plural_len,
+ true,
+ expr_is_from_block,
);
}
}
@@ -407,6 +467,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
descr_post,
plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
true,
+ expr_is_from_block,
);
}
MustUsePath::Closure(span) => {
@@ -433,8 +494,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
cx,
def_id: *def_id,
note: *reason,
- suggestion: (!is_inner)
- .then_some(UnusedDefSuggestion { span: span.shrink_to_lo() }),
+ suggestion: (!is_inner).then_some(if expr_is_from_block {
+ UnusedDefSuggestion::BlockTailExpr {
+ before_span: span.shrink_to_lo(),
+ after_span: span.shrink_to_hi(),
+ }
+ } else {
+ UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
+ }),
},
);
}
@@ -556,6 +623,7 @@ trait UnusedDelimLint {
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
+ is_kw: bool,
);
fn is_expr_delims_necessary(
@@ -624,6 +692,7 @@ trait UnusedDelimLint {
ctx: UnusedDelimsCtx,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
+ is_kw: bool,
) {
// If `value` has `ExprKind::Err`, unused delim lint can be broken.
// For example, the following code caused ICE.
@@ -667,7 +736,7 @@ trait UnusedDelimLint {
left_pos.is_some_and(|s| s >= value.span.lo()),
right_pos.is_some_and(|s| s <= value.span.hi()),
);
- self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space);
+ self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
}
fn emit_unused_delims(
@@ -677,6 +746,7 @@ trait UnusedDelimLint {
spans: Option<(Span, Span)>,
msg: &str,
keep_space: (bool, bool),
+ is_kw: bool,
) {
let primary_span = if let Some((lo, hi)) = spans {
if hi.is_empty() {
@@ -690,7 +760,7 @@ trait UnusedDelimLint {
let suggestion = spans.map(|(lo, hi)| {
let sm = cx.sess().source_map();
let lo_replace =
- if keep_space.0 &&
+ if (keep_space.0 || is_kw) &&
let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') {
" "
} else {
@@ -720,7 +790,7 @@ trait UnusedDelimLint {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
use rustc_ast::ExprKind::*;
- let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
+ let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
// Do not lint `unused_braces` in `if let` expressions.
If(ref cond, ref block, _)
if !matches!(cond.kind, Let(_, _, _))
@@ -728,7 +798,7 @@ trait UnusedDelimLint {
{
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
- (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right))
+ (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
}
// Do not lint `unused_braces` in `while let` expressions.
@@ -738,27 +808,27 @@ trait UnusedDelimLint {
{
let left = e.span.lo() + rustc_span::BytePos(5);
let right = block.span.lo();
- (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right))
+ (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
}
ForLoop(_, ref cond, ref block, ..) => {
- (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()))
+ (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()), true)
}
Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
let left = e.span.lo() + rustc_span::BytePos(5);
- (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None)
+ (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
}
Ret(Some(ref value)) => {
let left = e.span.lo() + rustc_span::BytePos(3);
- (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None)
+ (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
}
- Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None),
+ Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
Assign(_, ref value, _) | AssignOp(.., ref value) => {
- (value, UnusedDelimsCtx::AssignedValue, false, None, None)
+ (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
}
// either function/method call, or something this lint doesn't care about
ref call_or_other => {
@@ -778,12 +848,20 @@ trait UnusedDelimLint {
return;
}
for arg in args_to_check {
- self.check_unused_delims_expr(cx, arg, ctx, false, None, None);
+ self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
}
return;
}
};
- self.check_unused_delims_expr(cx, &value, ctx, followed_by_block, left_pos, right_pos);
+ self.check_unused_delims_expr(
+ cx,
+ &value,
+ ctx,
+ followed_by_block,
+ left_pos,
+ right_pos,
+ is_kw,
+ );
}
fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
@@ -794,7 +872,7 @@ trait UnusedDelimLint {
None => UnusedDelimsCtx::AssignedValue,
Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
};
- self.check_unused_delims_expr(cx, init, ctx, false, None, None);
+ self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
}
}
StmtKind::Expr(ref expr) => {
@@ -805,6 +883,7 @@ trait UnusedDelimLint {
false,
None,
None,
+ false,
);
}
_ => {}
@@ -824,6 +903,7 @@ trait UnusedDelimLint {
false,
None,
None,
+ false,
);
}
}
@@ -879,6 +959,7 @@ impl UnusedDelimLint for UnusedParens {
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
+ is_kw: bool,
) {
match value.kind {
ast::ExprKind::Paren(ref inner) => {
@@ -893,7 +974,7 @@ impl UnusedDelimLint for UnusedParens {
_,
) if node.lazy()))
{
- self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
+ self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
}
}
ast::ExprKind::Let(_, ref expr, _) => {
@@ -904,6 +985,7 @@ impl UnusedDelimLint for UnusedParens {
followed_by_block,
None,
None,
+ false,
);
}
_ => {}
@@ -942,7 +1024,7 @@ impl UnusedParens {
.span
.find_ancestor_inside(value.span)
.map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())));
- self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space);
+ self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
}
}
}
@@ -967,6 +1049,7 @@ impl EarlyLintPass for UnusedParens {
true,
None,
None,
+ true,
);
for stmt in &block.stmts {
<Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
@@ -985,6 +1068,7 @@ impl EarlyLintPass for UnusedParens {
false,
None,
None,
+ true,
);
}
}
@@ -1043,6 +1127,7 @@ impl EarlyLintPass for UnusedParens {
false,
None,
None,
+ false,
);
}
ast::TyKind::Paren(r) => {
@@ -1057,7 +1142,7 @@ impl EarlyLintPass for UnusedParens {
.find_ancestor_inside(ty.span)
.map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())));
- self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
}
}
self.with_self_ty_parens = false;
@@ -1130,6 +1215,7 @@ impl UnusedDelimLint for UnusedBraces {
followed_by_block: bool,
left_pos: Option<BytePos>,
right_pos: Option<BytePos>,
+ is_kw: bool,
) {
match value.kind {
ast::ExprKind::Block(ref inner, None)
@@ -1170,7 +1256,7 @@ impl UnusedDelimLint for UnusedBraces {
&& !value.span.from_expansion()
&& !inner.span.from_expansion()
{
- self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
+ self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
}
}
}
@@ -1183,6 +1269,7 @@ impl UnusedDelimLint for UnusedBraces {
followed_by_block,
None,
None,
+ false,
);
}
_ => {}
@@ -1207,6 +1294,7 @@ impl EarlyLintPass for UnusedBraces {
false,
None,
None,
+ false,
);
}
}
@@ -1220,6 +1308,7 @@ impl EarlyLintPass for UnusedBraces {
false,
None,
None,
+ false,
);
}
}
@@ -1233,6 +1322,7 @@ impl EarlyLintPass for UnusedBraces {
false,
None,
None,
+ false,
);
}
}
@@ -1247,6 +1337,7 @@ impl EarlyLintPass for UnusedBraces {
false,
None,
None,
+ false,
);
}
@@ -1258,6 +1349,7 @@ impl EarlyLintPass for UnusedBraces {
false,
None,
None,
+ false,
);
}
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 6e9dc880a..87c542dc2 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3272,6 +3272,43 @@ declare_lint! {
"ambiguous glob re-exports",
}
+declare_lint! {
+ /// The `hidden_glob_reexports` lint detects cases where glob re-export items are shadowed by
+ /// private items.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// #![deny(hidden_glob_reexports)]
+ ///
+ /// pub mod upstream {
+ /// mod inner { pub struct Foo {}; pub struct Bar {}; }
+ /// pub use self::inner::*;
+ /// struct Foo {} // private item shadows `inner::Foo`
+ /// }
+ ///
+ /// // mod downstream {
+ /// // fn test() {
+ /// // let _ = crate::upstream::Foo; // inaccessible
+ /// // }
+ /// // }
+ ///
+ /// pub fn main() {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// This was previously accepted without any errors or warnings but it could silently break a
+ /// crate's downstream user code. If the `struct Foo` was added, `dep::inner::Foo` would
+ /// silently become inaccessible and trigger a "`struct `Foo` is private`" visibility error at
+ /// the downstream use site.
+ pub HIDDEN_GLOB_REEXPORTS,
+ Warn,
+ "name introduced by a private item shadows a name introduced by a public glob re-export",
+}
+
declare_lint_pass! {
/// Does nothing as a lint pass, but registers some `Lint`s
/// that are used by other parts of the compiler.
@@ -3304,6 +3341,7 @@ declare_lint_pass! {
FORBIDDEN_LINT_GROUPS,
FUNCTION_ITEM_REFERENCES,
FUZZY_PROVENANCE_CASTS,
+ HIDDEN_GLOB_REEXPORTS,
ILL_FORMED_ATTRIBUTE_INPUT,
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
IMPLIED_BOUNDS_ENTAILMENT,
@@ -3319,6 +3357,7 @@ declare_lint_pass! {
LARGE_ASSIGNMENTS,
LATE_BOUND_LIFETIME_ARGUMENTS,
LEGACY_DERIVE_HELPERS,
+ LONG_RUNNING_CONST_EVAL,
LOSSY_PROVENANCE_CASTS,
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
MACRO_USE_EXTERN_CRATE,
@@ -3333,7 +3372,9 @@ declare_lint_pass! {
OVERLAPPING_RANGE_ENDPOINTS,
PATTERNS_IN_FNS_WITHOUT_BODY,
POINTER_STRUCTURAL_MATCH,
+ PRIVATE_BOUNDS,
PRIVATE_IN_PUBLIC,
+ PRIVATE_INTERFACES,
PROC_MACRO_BACK_COMPAT,
PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
@@ -3360,6 +3401,7 @@ declare_lint_pass! {
UNINHABITED_STATIC,
UNKNOWN_CRATE_TYPES,
UNKNOWN_LINTS,
+ UNNAMEABLE_TYPES,
UNREACHABLE_CODE,
UNREACHABLE_PATTERNS,
UNSAFE_OP_IN_UNSAFE_FN,
@@ -3367,6 +3409,7 @@ declare_lint_pass! {
UNSTABLE_SYNTAX_PRE_EXPANSION,
UNSUPPORTED_CALLING_CONVENTIONS,
UNUSED_ASSIGNMENTS,
+ UNUSED_ASSOCIATED_TYPE_BOUNDS,
UNUSED_ATTRIBUTES,
UNUSED_CRATE_DEPENDENCIES,
UNUSED_EXTERN_CRATES,
@@ -3389,6 +3432,70 @@ declare_lint_pass! {
}
declare_lint! {
+ /// The `long_running_const_eval` lint is emitted when const
+ /// eval is running for a long time to ensure rustc terminates
+ /// even if you accidentally wrote an infinite loop.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// const FOO: () = loop {};
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Loops allow const evaluation to compute arbitrary code, but may also
+ /// cause infinite loops or just very long running computations.
+ /// Users can enable long running computations by allowing the lint
+ /// on individual constants or for entire crates.
+ ///
+ /// ### Unconditional warnings
+ ///
+ /// Note that regardless of whether the lint is allowed or set to warn,
+ /// the compiler will issue warnings if constant evaluation runs significantly
+ /// longer than this lint's limit. These warnings are also shown to downstream
+ /// users from crates.io or similar registries. If you are above the lint's limit,
+ /// both you and downstream users might be exposed to these warnings.
+ /// They might also appear on compiler updates, as the compiler makes minor changes
+ /// about how complexity is measured: staying below the limit ensures that there
+ /// is enough room, and given that the lint is disabled for people who use your
+ /// dependency it means you will be the only one to get the warning and can put
+ /// out an update in your own time.
+ pub LONG_RUNNING_CONST_EVAL,
+ Deny,
+ "detects long const eval operations",
+ report_in_external_macro
+}
+
+declare_lint! {
+ /// The `unused_associated_type_bounds` lint is emitted when an
+ /// associated type bound is added to a trait object, but the associated
+ /// type has a `where Self: Sized` bound, and is thus unavailable on the
+ /// trait object anyway.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// trait Foo {
+ /// type Bar where Self: Sized;
+ /// }
+ /// type Mop = dyn Foo<Bar = ()>;
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Just like methods with `Self: Sized` bounds are unavailable on trait
+ /// objects, associated types can be removed from the trait object.
+ pub UNUSED_ASSOCIATED_TYPE_BOUNDS,
+ Warn,
+ "detects unused `Foo = Bar` bounds in `dyn Trait<Foo = Bar>`"
+}
+
+declare_lint! {
/// The `unused_doc_comments` lint detects doc comments that aren't used
/// by `rustdoc`.
///
@@ -4175,3 +4282,101 @@ declare_lint! {
Warn,
"\"invalid_parameter\" isn't a valid argument for `#[macro_export]`",
}
+
+declare_lint! {
+ /// The `private_interfaces` lint detects types in a primary interface of an item,
+ /// that are more private than the item itself. Primary interface of an item is all
+ /// its interface except for bounds on generic parameters and where clauses.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// # #![feature(type_privacy_lints)]
+ /// # #![allow(unused)]
+ /// # #![allow(private_in_public)]
+ /// #![deny(private_interfaces)]
+ /// struct SemiPriv;
+ ///
+ /// mod m1 {
+ /// struct Priv;
+ /// impl crate::SemiPriv {
+ /// pub fn f(_: Priv) {}
+ /// }
+ /// }
+ ///
+ /// # fn main() {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Having something private in primary interface guarantees that
+ /// the item will be unusable from outer modules due to type privacy.
+ pub PRIVATE_INTERFACES,
+ Allow,
+ "private type in primary interface of an item",
+ @feature_gate = sym::type_privacy_lints;
+}
+
+declare_lint! {
+ /// The `private_bounds` lint detects types in a secondary interface of an item,
+ /// that are more private than the item itself. Secondary interface of an item consists of
+ /// bounds on generic parameters and where clauses, including supertraits for trait items.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// # #![feature(type_privacy_lints)]
+ /// # #![allow(private_in_public)]
+ /// # #![allow(unused)]
+ /// #![deny(private_bounds)]
+ ///
+ /// struct PrivTy;
+ /// pub struct S
+ /// where PrivTy:
+ /// {}
+ /// # fn main() {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Having private types or traits in item bounds makes it less clear what interface
+ /// the item actually provides.
+ pub PRIVATE_BOUNDS,
+ Allow,
+ "private type in secondary interface of an item",
+ @feature_gate = sym::type_privacy_lints;
+}
+
+declare_lint! {
+ /// The `unnameable_types` lint detects types for which you can get objects of that type,
+ /// but cannot name the type itself.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// # #![feature(type_privacy_lints)]
+ /// # #![allow(unused)]
+ /// #![deny(unnameable_types)]
+ /// mod m {
+ /// pub struct S;
+ /// }
+ ///
+ /// pub fn get_voldemort() -> m::S { m::S }
+ /// # fn main() {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// It is often expected that if you can obtain an object of type `T`, then
+ /// you can name the type `T` as well, this lint attempts to enforce this rule.
+ pub UNNAMEABLE_TYPES,
+ Allow,
+ "effective visibility of a type is larger than the area in which it can be named",
+ @feature_gate = sym::type_privacy_lints;
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index e27e322db..5a5031b79 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -540,6 +540,16 @@ pub enum BuiltinLintDiagnostics {
/// Span where the same name is also re-exported.
duplicate_reexport_span: Span,
},
+ HiddenGlobReexports {
+ /// The name of the local binding which shadows the glob re-export.
+ name: String,
+ /// The namespace for which the shadowing occurred in.
+ namespace: String,
+ /// The glob reexport that is shadowed by the local binding.
+ glob_reexport_span: Span,
+ /// The local binding that shadows the glob reexport.
+ private_item_span: Span,
+ },
}
/// Lints that are buffered up early on in the `Session` before the
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
index 058906283..af6f4d5ea 100644
--- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -96,6 +96,7 @@ enum LLVMRustAttribute {
AllocatedPointer = 38,
AllocAlign = 39,
#endif
+ SanitizeSafeStack = 40,
};
typedef struct OpaqueRustString *RustStringRef;
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 49acd71b3..bb7510b3a 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -7,7 +7,12 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicsARM.h"
+#include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/Mangler.h"
+#include "llvm/Remarks/RemarkStreamer.h"
+#include "llvm/Remarks/RemarkSerializer.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Support/ToolOutputFile.h"
#if LLVM_VERSION_GE(16, 0)
#include "llvm/Support/ModRef.h"
#endif
@@ -116,6 +121,32 @@ extern "C" LLVMValueRef LLVMRustGetNamedValue(LLVMModuleRef M, const char *Name,
return wrap(unwrap(M)->getNamedValue(StringRef(Name, NameLen)));
}
+enum class LLVMRustTailCallKind {
+ None,
+ Tail,
+ MustTail,
+ NoTail,
+};
+
+static CallInst::TailCallKind fromRust(LLVMRustTailCallKind Kind) {
+ switch (Kind) {
+ case LLVMRustTailCallKind::None:
+ return CallInst::TailCallKind::TCK_None;
+ case LLVMRustTailCallKind::Tail:
+ return CallInst::TailCallKind::TCK_Tail;
+ case LLVMRustTailCallKind::MustTail:
+ return CallInst::TailCallKind::TCK_MustTail;
+ case LLVMRustTailCallKind::NoTail:
+ return CallInst::TailCallKind::TCK_NoTail;
+ default:
+ report_fatal_error("bad CallInst::TailCallKind.");
+ }
+}
+
+extern "C" void LLVMRustSetTailCallKind(LLVMValueRef Call, LLVMRustTailCallKind TCK) {
+ unwrap<CallInst>(Call)->setTailCallKind(fromRust(TCK));
+}
+
extern "C" LLVMValueRef LLVMRustGetOrInsertFunction(LLVMModuleRef M,
const char *Name,
size_t NameLen,
@@ -234,6 +265,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
case AllocAlign:
return Attribute::AllocAlign;
#endif
+ case SanitizeSafeStack:
+ return Attribute::SafeStack;
}
report_fatal_error("bad AttributeKind");
}
@@ -1853,23 +1886,44 @@ using LLVMDiagnosticHandlerTy = DiagnosticHandler::DiagnosticHandlerTy;
// When RemarkAllPasses is true, remarks are enabled for all passes. Otherwise
// the RemarkPasses array specifies individual passes for which remarks will be
// enabled.
+//
+// If RemarkFilePath is not NULL, optimization remarks will be streamed directly into this file,
+// bypassing the diagnostics handler.
extern "C" void LLVMRustContextConfigureDiagnosticHandler(
LLVMContextRef C, LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
void *DiagnosticHandlerContext, bool RemarkAllPasses,
- const char * const * RemarkPasses, size_t RemarkPassesLen) {
+ const char * const * RemarkPasses, size_t RemarkPassesLen,
+ const char * RemarkFilePath
+) {
class RustDiagnosticHandler final : public DiagnosticHandler {
public:
- RustDiagnosticHandler(LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
- void *DiagnosticHandlerContext,
- bool RemarkAllPasses,
- std::vector<std::string> RemarkPasses)
+ RustDiagnosticHandler(
+ LLVMDiagnosticHandlerTy DiagnosticHandlerCallback,
+ void *DiagnosticHandlerContext,
+ bool RemarkAllPasses,
+ std::vector<std::string> RemarkPasses,
+ std::unique_ptr<ToolOutputFile> RemarksFile,
+ std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer,
+ std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer
+ )
: DiagnosticHandlerCallback(DiagnosticHandlerCallback),
DiagnosticHandlerContext(DiagnosticHandlerContext),
RemarkAllPasses(RemarkAllPasses),
- RemarkPasses(RemarkPasses) {}
+ RemarkPasses(std::move(RemarkPasses)),
+ RemarksFile(std::move(RemarksFile)),
+ RemarkStreamer(std::move(RemarkStreamer)),
+ LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
+ if (this->LlvmRemarkStreamer) {
+ if (auto *OptDiagBase = dyn_cast<DiagnosticInfoOptimizationBase>(&DI)) {
+ if (OptDiagBase->isEnabled()) {
+ this->LlvmRemarkStreamer->emit(*OptDiagBase);
+ return true;
+ }
+ }
+ }
if (DiagnosticHandlerCallback) {
DiagnosticHandlerCallback(DI, DiagnosticHandlerContext);
return true;
@@ -1910,14 +1964,64 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
bool RemarkAllPasses = false;
std::vector<std::string> RemarkPasses;
+
+ // Since LlvmRemarkStreamer contains a pointer to RemarkStreamer, the ordering of the three
+ // members below is important.
+ std::unique_ptr<ToolOutputFile> RemarksFile;
+ std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer;
+ std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer;
};
std::vector<std::string> Passes;
for (size_t I = 0; I != RemarkPassesLen; ++I)
+ {
Passes.push_back(RemarkPasses[I]);
+ }
+
+ // We need to hold onto both the streamers and the opened file
+ std::unique_ptr<ToolOutputFile> RemarkFile;
+ std::unique_ptr<llvm::remarks::RemarkStreamer> RemarkStreamer;
+ std::unique_ptr<LLVMRemarkStreamer> LlvmRemarkStreamer;
+
+ if (RemarkFilePath != nullptr) {
+ std::error_code EC;
+ RemarkFile = std::make_unique<ToolOutputFile>(
+ RemarkFilePath,
+ EC,
+ llvm::sys::fs::OF_TextWithCRLF
+ );
+ if (EC) {
+ std::string Error = std::string("Cannot create remark file: ") +
+ toString(errorCodeToError(EC));
+ report_fatal_error(Twine(Error));
+ }
+
+ // Do not delete the file after we gather remarks
+ RemarkFile->keep();
+
+ auto RemarkSerializer = remarks::createRemarkSerializer(
+ llvm::remarks::Format::YAML,
+ remarks::SerializerMode::Separate,
+ RemarkFile->os()
+ );
+ if (Error E = RemarkSerializer.takeError())
+ {
+ std::string Error = std::string("Cannot create remark serializer: ") + toString(std::move(E));
+ report_fatal_error(Twine(Error));
+ }
+ RemarkStreamer = std::make_unique<llvm::remarks::RemarkStreamer>(std::move(*RemarkSerializer));
+ LlvmRemarkStreamer = std::make_unique<LLVMRemarkStreamer>(*RemarkStreamer);
+ }
unwrap(C)->setDiagnosticHandler(std::make_unique<RustDiagnosticHandler>(
- DiagnosticHandlerCallback, DiagnosticHandlerContext, RemarkAllPasses, Passes));
+ DiagnosticHandlerCallback,
+ DiagnosticHandlerContext,
+ RemarkAllPasses,
+ Passes,
+ std::move(RemarkFile),
+ std::move(RemarkStreamer),
+ std::move(LlvmRemarkStreamer)
+ ));
}
extern "C" void LLVMRustGetMangledName(LLVMValueRef V, RustStringRef Str) {
diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml
index 7f955b0a7..aa6e46cd8 100644
--- a/compiler/rustc_log/Cargo.toml
+++ b/compiler/rustc_log/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
tracing = "0.1.28"
tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
tracing-tree = "0.2.0"
-tracing-core = "0.1.28"
+tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635
[dev-dependencies]
rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml
index 1f1201b00..16c4a8500 100644
--- a/compiler/rustc_macros/Cargo.toml
+++ b/compiler/rustc_macros/Cargo.toml
@@ -8,6 +8,7 @@ proc-macro = true
[dependencies]
synstructure = "0.13.0"
-syn = { version = "2", features = ["full"] }
+# FIXME(Nilstrieb): Updating this causes changes in the diagnostics output.
+syn = { version = "=2.0.8", features = ["full"] }
proc-macro2 = "1"
quote = "1"
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 12a954258..04b7c5fee 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -1,5 +1,7 @@
#![deny(unused_must_use)]
+use std::cell::RefCell;
+
use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind};
use crate::diagnostics::error::{span_err, DiagnosticDeriveError};
use crate::diagnostics::utils::SetOnce;
@@ -28,6 +30,7 @@ impl<'a> DiagnosticDerive<'a> {
pub(crate) fn into_tokens(self) -> TokenStream {
let DiagnosticDerive { mut structure, mut builder } = self;
+ let slugs = RefCell::new(Vec::new());
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
let preamble = builder.preamble(variant);
let body = builder.body(variant);
@@ -56,6 +59,7 @@ impl<'a> DiagnosticDerive<'a> {
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
Some(slug) => {
+ slugs.borrow_mut().push(slug.clone());
quote! {
let mut #diag = #handler.struct_diagnostic(crate::fluent_generated::#slug);
}
@@ -73,7 +77,8 @@ impl<'a> DiagnosticDerive<'a> {
});
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
- structure.gen_impl(quote! {
+
+ let mut imp = structure.gen_impl(quote! {
gen impl<'__diagnostic_handler_sess, G>
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
for @Self
@@ -89,7 +94,11 @@ impl<'a> DiagnosticDerive<'a> {
#implementation
}
}
- })
+ });
+ for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) {
+ imp.extend(test);
+ }
+ imp
}
}
@@ -124,6 +133,7 @@ impl<'a> LintDiagnosticDerive<'a> {
}
});
+ let slugs = RefCell::new(Vec::new());
let msg = builder.each_variant(&mut structure, |mut builder, variant| {
// Collect the slug by generating the preamble.
let _ = builder.preamble(variant);
@@ -148,6 +158,7 @@ impl<'a> LintDiagnosticDerive<'a> {
DiagnosticDeriveError::ErrorHandled.to_compile_error()
}
Some(slug) => {
+ slugs.borrow_mut().push(slug.clone());
quote! {
crate::fluent_generated::#slug.into()
}
@@ -156,7 +167,7 @@ impl<'a> LintDiagnosticDerive<'a> {
});
let diag = &builder.diag;
- structure.gen_impl(quote! {
+ let mut imp = structure.gen_impl(quote! {
gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self {
#[track_caller]
fn decorate_lint<'__b>(
@@ -171,7 +182,12 @@ impl<'a> LintDiagnosticDerive<'a> {
#msg
}
}
- })
+ });
+ for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) {
+ imp.extend(test);
+ }
+
+ imp
}
}
@@ -198,3 +214,40 @@ impl Mismatch {
}
}
}
+
+/// Generates a `#[test]` that verifies that all referenced variables
+/// exist on this structure.
+fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream {
+ // FIXME: We can't identify variables in a subdiagnostic
+ for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) {
+ for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) {
+ if attr_name == "subdiagnostic" {
+ return quote!();
+ }
+ }
+ }
+ use std::sync::atomic::{AtomicUsize, Ordering};
+ // We need to make sure that the same diagnostic slug can be used multiple times without causing an
+ // error, so just have a global counter here.
+ static COUNTER: AtomicUsize = AtomicUsize::new(0);
+ let slug = slug.get_ident().unwrap();
+ let ident = quote::format_ident!("verify_{slug}_{}", COUNTER.fetch_add(1, Ordering::Relaxed));
+ let ref_slug = quote::format_ident!("{slug}_refs");
+ let struct_name = &structure.ast().ident;
+ let variables: Vec<_> = structure
+ .variants()
+ .iter()
+ .flat_map(|v| v.ast().fields.iter().filter_map(|f| f.ident.as_ref().map(|i| i.to_string())))
+ .collect();
+ // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this
+ quote! {
+ #[cfg(test)]
+ #[test ]
+ fn #ident() {
+ let variables = [#(#variables),*];
+ for vref in crate::fluent_generated::#ref_slug {
+ assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug));
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index cd6e36874..2e6e84ad8 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -14,6 +14,8 @@ use syn::Token;
use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type};
use synstructure::{BindingInfo, Structure, VariantInfo};
+use super::utils::SubdiagnosticVariant;
+
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
#[derive(Clone, PartialEq, Eq)]
pub(crate) enum DiagnosticDeriveKind {
@@ -150,19 +152,19 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
fn parse_subdiag_attribute(
&self,
attr: &Attribute,
- ) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
- let Some((subdiag, slug)) = SubdiagnosticKind::from_attr(attr, self)? else {
+ ) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
+ let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
return Ok(None);
};
- if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag {
+ if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag.kind {
throw_invalid_attr!(attr, |diag| diag
.help("consider creating a `Subdiagnostic` instead"));
}
- let slug = slug.unwrap_or_else(|| match subdiag {
+ let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind {
SubdiagnosticKind::Label => parse_quote! { _subdiag::label },
SubdiagnosticKind::Note => parse_quote! { _subdiag::note },
SubdiagnosticKind::Help => parse_quote! { _subdiag::help },
@@ -171,7 +173,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
});
- Ok(Some((subdiag, slug)))
+ Ok(Some((subdiag.kind, slug, subdiag.no_span)))
}
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@@ -229,7 +231,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
return Ok(tokens);
}
- let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else {
+ let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
return Ok(quote! {});
@@ -380,7 +382,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
_ => (),
}
- let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else {
+ let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
return Ok(quote! {});
diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs
index b37dc826d..84b18a620 100644
--- a/compiler/rustc_macros/src/diagnostics/error.rs
+++ b/compiler/rustc_macros/src/diagnostics/error.rs
@@ -54,7 +54,7 @@ fn path_to_string(path: &syn::Path) -> String {
/// Returns an error diagnostic on span `span` with msg `msg`.
#[must_use]
-pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
+pub(crate) fn span_err<T: Into<String>>(span: impl MultiSpan, msg: T) -> Diagnostic {
Diagnostic::spanned(span, Level::Error, msg)
}
@@ -77,11 +77,9 @@ pub(crate) fn invalid_attr(attr: &Attribute) -> Diagnostic {
let span = attr.span().unwrap();
let path = path_to_string(attr.path());
match attr.meta {
- Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")),
- Meta::NameValue(_) => {
- span_err(span, &format!("`#[{path} = ...]` is not a valid attribute"))
- }
- Meta::List(_) => span_err(span, &format!("`#[{path}(...)]` is not a valid attribute")),
+ Meta::Path(_) => span_err(span, format!("`#[{path}]` is not a valid attribute")),
+ Meta::NameValue(_) => span_err(span, format!("`#[{path} = ...]` is not a valid attribute")),
+ Meta::List(_) => span_err(span, format!("`#[{path}(...)]` is not a valid attribute")),
}
}
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 374ba1a45..e8dc98691 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -14,6 +14,8 @@ use quote::{format_ident, quote};
use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path};
use synstructure::{BindingInfo, Structure, VariantInfo};
+use super::utils::SubdiagnosticVariant;
+
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
pub(crate) struct SubdiagnosticDeriveBuilder {
diag: syn::Ident,
@@ -180,11 +182,13 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
}
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
- fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
+ fn identify_kind(
+ &mut self,
+ ) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
let mut kind_slugs = vec![];
for attr in self.variant.ast().attrs {
- let Some((kind, slug)) = SubdiagnosticKind::from_attr(attr, self)? else {
+ let Some(SubdiagnosticVariant { kind, slug, no_span }) = SubdiagnosticVariant::from_attr(attr, self)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
continue;
@@ -196,13 +200,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
throw_span_err!(
attr.span().unwrap(),
- &format!(
+ format!(
"diagnostic slug must be first argument of a `#[{name}(...)]` attribute"
)
);
};
- kind_slugs.push((kind, slug));
+ kind_slugs.push((kind, slug, no_span));
}
Ok(kind_slugs)
@@ -487,7 +491,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
}
};
- let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
+ let kind_stats: KindsStatistics =
+ kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
let init = if kind_stats.has_multipart_suggestion {
quote! { let mut suggestions = Vec::new(); }
@@ -508,13 +513,17 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let diag = &self.parent.diag;
let f = &self.parent.f;
let mut calls = TokenStream::new();
- for (kind, slug) in kind_slugs {
+ for (kind, slug, no_span) in kind_slugs {
let message = format_ident!("__message");
calls.extend(
quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); },
);
- let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
+ let name = format_ident!(
+ "{}{}",
+ if span_field.is_some() && !no_span { "span_" } else { "" },
+ kind
+ );
let call = match kind {
SubdiagnosticKind::Suggestion {
suggestion_kind,
@@ -566,7 +575,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
}
}
_ => {
- if let Some(span) = span_field {
+ if let Some(span) = span_field && !no_span {
quote! { #diag.#name(#span, #message); }
} else {
quote! { #diag.#name(#message); }
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index e2434981f..125632921 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -347,7 +347,7 @@ pub(crate) trait HasFieldMap {
None => {
span_err(
span.unwrap(),
- &format!("`{field}` doesn't refer to a field on this type"),
+ format!("`{field}` doesn't refer to a field on this type"),
)
.emit();
quote! {
@@ -597,14 +597,20 @@ pub(super) enum SubdiagnosticKind {
},
}
-impl SubdiagnosticKind {
- /// Constructs a `SubdiagnosticKind` from a field or type attribute such as `#[note]`,
- /// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the
+pub(super) struct SubdiagnosticVariant {
+ pub(super) kind: SubdiagnosticKind,
+ pub(super) slug: Option<Path>,
+ pub(super) no_span: bool,
+}
+
+impl SubdiagnosticVariant {
+ /// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
+ /// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the
/// `SubdiagnosticKind` and the diagnostic slug, if specified.
pub(super) fn from_attr(
attr: &Attribute,
fields: &impl HasFieldMap,
- ) -> Result<Option<(SubdiagnosticKind, Option<Path>)>, DiagnosticDeriveError> {
+ ) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
// Always allow documentation comments.
if is_doc_comment(attr) {
return Ok(None);
@@ -679,7 +685,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::Help
| SubdiagnosticKind::Warn
| SubdiagnosticKind::MultipartSuggestion { .. } => {
- return Ok(Some((kind, None)));
+ return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false }));
}
SubdiagnosticKind::Suggestion { .. } => {
throw_span_err!(span, "suggestion without `code = \"...\"`")
@@ -696,11 +702,14 @@ impl SubdiagnosticKind {
let mut first = true;
let mut slug = None;
+ let mut no_span = false;
list.parse_nested_meta(|nested| {
if nested.input.is_empty() || nested.input.peek(Token![,]) {
if first {
slug = Some(nested.path);
+ } else if nested.path.is_ident("no_span") {
+ no_span = true;
} else {
span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
}
@@ -775,19 +784,19 @@ impl SubdiagnosticKind {
(_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
.help(
- "only `style`, `code` and `applicability` are valid nested attributes",
+ "only `no_span`, `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
has_errors = true;
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
- .help("only `style` and `applicability` are valid nested attributes")
+ .help("only `no_span`, `style` and `applicability` are valid nested attributes")
.emit();
has_errors = true;
}
_ => {
- span_err(path_span, "invalid nested attribute").emit();
+ span_err(path_span, "only `no_span` is a valid nested attribute").emit();
has_errors = true;
}
}
@@ -831,7 +840,7 @@ impl SubdiagnosticKind {
| SubdiagnosticKind::Warn => {}
}
- Ok(Some((kind, slug)))
+ Ok(Some(SubdiagnosticVariant { kind, slug, no_span }))
}
}
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index 6d8601b9e..13b3dac85 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -4,6 +4,9 @@ metadata_as_needed_compatibility =
metadata_bad_panic_strategy =
the linked panic runtime `{$runtime}` is not compiled with this crate's panic strategy `{$strategy}`
+metadata_binary_output_to_tty =
+ option `-o` or `--emit` is used to write binary output type `metadata` to stdout, but stdout is a tty
+
metadata_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
@@ -63,6 +66,9 @@ metadata_fail_seek_file =
metadata_fail_write_file =
failed to write to the file: {$err}
+metadata_failed_copy_to_stdout =
+ failed to copy {$filename} to stdout: {$err}
+
metadata_failed_create_encoded_metadata =
failed to create encoded metadata from file: {$err}
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index aaf72ab94..b3976d756 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -365,6 +365,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
lib: Library,
dep_kind: CrateDepKind,
name: Symbol,
+ private_dep: Option<bool>,
) -> Result<CrateNum, CrateError> {
let _prof_timer = self.sess.prof.generic_activity("metadata_register_crate");
@@ -372,8 +373,13 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
let crate_root = metadata.get_root();
let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());
- let private_dep =
- self.sess.opts.externs.get(name.as_str()).is_some_and(|e| e.is_private_dep);
+ let private_dep = self
+ .sess
+ .opts
+ .externs
+ .get(name.as_str())
+ .map_or(private_dep.unwrap_or(false), |e| e.is_private_dep)
+ && private_dep.unwrap_or(true);
// Claim this crate number and cache it
let cnum = self.cstore.intern_stable_crate_id(&crate_root)?;
@@ -518,15 +524,16 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
if !name.as_str().is_ascii() {
return Err(CrateError::NonAsciiName(name));
}
- let (root, hash, host_hash, extra_filename, path_kind) = match dep {
+ let (root, hash, host_hash, extra_filename, path_kind, private_dep) = match dep {
Some((root, dep)) => (
Some(root),
Some(dep.hash),
dep.host_hash,
Some(&dep.extra_filename[..]),
PathKind::Dependency,
+ Some(dep.is_private),
),
- None => (None, None, None, None, PathKind::Crate),
+ None => (None, None, None, None, PathKind::Crate, None),
};
let result = if let Some(cnum) = self.existing_match(name, hash, path_kind) {
(LoadResult::Previous(cnum), None)
@@ -562,10 +569,13 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
dep_kind = CrateDepKind::MacrosOnly;
}
data.update_dep_kind(|data_dep_kind| cmp::max(data_dep_kind, dep_kind));
+ if let Some(private_dep) = private_dep {
+ data.update_and_private_dep(private_dep);
+ }
Ok(cnum)
}
(LoadResult::Loaded(library), host_library) => {
- self.register_crate(host_library, root, library, dep_kind, name)
+ self.register_crate(host_library, root, library, dep_kind, name, private_dep)
}
_ => panic!(),
}
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index a44c1dd58..fca06c0f4 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -396,6 +396,17 @@ pub struct FailedWriteError {
}
#[derive(Diagnostic)]
+#[diag(metadata_failed_copy_to_stdout)]
+pub struct FailedCopyToStdout {
+ pub filename: PathBuf,
+ pub err: Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(metadata_binary_output_to_tty)]
+pub struct BinaryOutputToTty;
+
+#[derive(Diagnostic)]
#[diag(metadata_missing_native_library)]
pub struct MissingNativeLibrary<'a> {
libname: &'a str,
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index 08de828fb..238f963ed 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -1,18 +1,19 @@
use crate::errors::{
- FailedCreateEncodedMetadata, FailedCreateFile, FailedCreateTempdir, FailedWriteError,
+ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile,
+ FailedCreateTempdir, FailedWriteError,
};
use crate::{encode_metadata, EncodedMetadata};
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
-use rustc_session::config::OutputType;
+use rustc_session::config::{OutFileName, OutputType};
use rustc_session::output::filename_for_metadata;
use rustc_session::{MetadataKind, Session};
use tempfile::Builder as TempFileBuilder;
-use std::fs;
use std::path::{Path, PathBuf};
+use std::{fs, io};
// FIXME(eddyb) maybe include the crate name in this?
pub const METADATA_FILENAME: &str = "lib.rmeta";
@@ -74,23 +75,37 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
// this file always exists.
let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
- if let Err(err) = non_durable_rename(&metadata_filename, &out_filename) {
- tcx.sess.emit_fatal(FailedWriteError { filename: out_filename, err });
- }
+ let filename = match out_filename {
+ OutFileName::Real(ref path) => {
+ if let Err(err) = non_durable_rename(&metadata_filename, path) {
+ tcx.sess.emit_fatal(FailedWriteError { filename: path.to_path_buf(), err });
+ }
+ path.clone()
+ }
+ OutFileName::Stdout => {
+ if out_filename.is_tty() {
+ tcx.sess.emit_err(BinaryOutputToTty);
+ } else if let Err(err) = copy_to_stdout(&metadata_filename) {
+ tcx.sess
+ .emit_err(FailedCopyToStdout { filename: metadata_filename.clone(), err });
+ }
+ metadata_filename
+ }
+ };
if tcx.sess.opts.json_artifact_notifications {
tcx.sess
.parse_sess
.span_diagnostic
- .emit_artifact_notification(&out_filename, "metadata");
+ .emit_artifact_notification(&out_filename.as_path(), "metadata");
}
- (out_filename, None)
+ (filename, None)
} else {
(metadata_filename, Some(metadata_tmpdir))
};
// Load metadata back to memory: codegen may need to include it in object files.
- let metadata =
- EncodedMetadata::from_path(metadata_filename, metadata_tmpdir).unwrap_or_else(|err| {
+ let metadata = EncodedMetadata::from_path(metadata_filename.clone(), metadata_tmpdir)
+ .unwrap_or_else(|err| {
tcx.sess.emit_fatal(FailedCreateEncodedMetadata { err });
});
@@ -116,3 +131,11 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> {
let _ = std::fs::remove_file(dst);
std::fs::rename(src, dst)
}
+
+pub fn copy_to_stdout(from: &Path) -> io::Result<()> {
+ let file = fs::File::open(from)?;
+ let mut reader = io::BufReader::new(file);
+ let mut stdout = io::stdout();
+ io::copy(&mut reader, &mut stdout)?;
+ Ok(())
+}
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 9f664d0f0..87373d997 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -1,6 +1,6 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(decl_macro)]
-#![feature(drain_filter)]
+#![feature(extract_if)]
#![feature(generators)]
#![feature(iter_from_generator)]
#![feature(let_chains)]
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index ceb348f34..a89d7b464 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -666,31 +666,30 @@ impl<'a> CrateLocator<'a> {
return None;
}
- let root = metadata.get_root();
- if root.is_proc_macro_crate() != self.is_proc_macro {
+ let header = metadata.get_header();
+ if header.is_proc_macro_crate != self.is_proc_macro {
info!(
"Rejecting via proc macro: expected {} got {}",
- self.is_proc_macro,
- root.is_proc_macro_crate(),
+ self.is_proc_macro, header.is_proc_macro_crate,
);
return None;
}
- if self.exact_paths.is_empty() && self.crate_name != root.name() {
+ if self.exact_paths.is_empty() && self.crate_name != header.name {
info!("Rejecting via crate name");
return None;
}
- if root.triple() != &self.triple {
- info!("Rejecting via crate triple: expected {} got {}", self.triple, root.triple());
+ if header.triple != self.triple {
+ info!("Rejecting via crate triple: expected {} got {}", self.triple, header.triple);
self.crate_rejections.via_triple.push(CrateMismatch {
path: libpath.to_path_buf(),
- got: root.triple().to_string(),
+ got: header.triple.to_string(),
});
return None;
}
- let hash = root.hash();
+ let hash = header.hash;
if let Some(expected_hash) = self.hash {
if hash != expected_hash {
info!("Rejecting via hash: expected {} got {}", expected_hash, hash);
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index c83c47e72..0dd7b1197 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -425,7 +425,7 @@ impl<'tcx> Collector<'tcx> {
// can move them to the end of the list below.
let mut existing = self
.libs
- .drain_filter(|lib| {
+ .extract_if(|lib| {
if lib.name.as_str() == passed_lib.name {
// FIXME: This whole logic is questionable, whether modifiers are
// involved or not, library reordering and kind overriding without
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index cc4e60cf6..b9318aee5 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::owned_slice::OwnedSlice;
use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc, OnceCell};
+use rustc_data_structures::sync::{AppendOnlyVec, AtomicBool, Lock, Lrc, OnceCell};
use rustc_data_structures::unhash::UnhashMap;
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro};
@@ -25,7 +25,7 @@ use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
use rustc_middle::ty::codec::TyDecoder;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::GeneratorDiagnosticData;
-use rustc_middle::ty::{self, ParameterizedOverTcx, Predicate, Ty, TyCtxt, Visibility};
+use rustc_middle::ty::{self, ParameterizedOverTcx, Ty, TyCtxt, Visibility};
use rustc_serialize::opaque::MemDecoder;
use rustc_serialize::{Decodable, Decoder};
use rustc_session::cstore::{
@@ -40,6 +40,7 @@ use proc_macro::bridge::client::ProcMacro;
use std::iter::TrustedLen;
use std::num::NonZeroUsize;
use std::path::Path;
+use std::sync::atomic::Ordering;
use std::{io, iter, mem};
pub(super) use cstore_impl::provide;
@@ -74,6 +75,7 @@ pub(crate) struct CrateMetadata {
blob: MetadataBlob,
// --- Some data pre-decoded from the metadata blob, usually for performance ---
+ /// Data about the top-level items in a crate, as well as various crate-level metadata.
root: CrateRoot,
/// Trait impl data.
/// FIXME: Used only from queries and can use query cache,
@@ -111,9 +113,10 @@ pub(crate) struct CrateMetadata {
dep_kind: Lock<CrateDepKind>,
/// Filesystem location of this crate.
source: Lrc<CrateSource>,
- /// Whether or not this crate should be consider a private dependency
- /// for purposes of the 'exported_private_dependencies' lint
- private_dep: bool,
+ /// Whether or not this crate should be consider a private dependency.
+ /// Used by the 'exported_private_dependencies' lint, and for determining
+ /// whether to emit suggestions that reference this crate.
+ private_dep: AtomicBool,
/// The hash for the host proc macro. Used to support `-Z dual-proc-macro`.
host_hash: Option<Svh>,
@@ -449,7 +452,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
};
- let cname = cdata.root.name;
+ let cname = cdata.root.name();
rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| {
debug!("SpecializedDecoder<SyntaxContext>: decoding {}", id);
cdata
@@ -564,7 +567,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
let cnum = u32::decode(decoder);
panic!(
"Decoding of crate {:?} tried to access proc-macro dep {:?}",
- decoder.cdata().root.name,
+ decoder.cdata().root.header.name,
cnum
);
}
@@ -633,7 +636,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Symbol {
}
}
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
+impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Clause<'tcx>, Span)] {
fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
ty::codec::RefDecodable::decode(d)
}
@@ -671,6 +674,16 @@ impl MetadataBlob {
.decode(self)
}
+ pub(crate) fn get_header(&self) -> CrateHeader {
+ let slice = &self.blob()[..];
+ let offset = METADATA_HEADER.len();
+
+ let pos_bytes = slice[offset..][..4].try_into().unwrap();
+ let pos = u32::from_be_bytes(pos_bytes) as usize;
+
+ LazyValue::<CrateHeader>::from_position(NonZeroUsize::new(pos).unwrap()).decode(self)
+ }
+
pub(crate) fn get_root(&self) -> CrateRoot {
let slice = &self.blob()[..];
let offset = METADATA_HEADER.len();
@@ -684,18 +697,19 @@ impl MetadataBlob {
pub(crate) fn list_crate_metadata(&self, out: &mut dyn io::Write) -> io::Result<()> {
let root = self.get_root();
writeln!(out, "Crate info:")?;
- writeln!(out, "name {}{}", root.name, root.extra_filename)?;
- writeln!(out, "hash {} stable_crate_id {:?}", root.hash, root.stable_crate_id)?;
+ writeln!(out, "name {}{}", root.name(), root.extra_filename)?;
+ writeln!(out, "hash {} stable_crate_id {:?}", root.hash(), root.stable_crate_id)?;
writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?;
writeln!(out, "=External Dependencies=")?;
for (i, dep) in root.crate_deps.decode(self).enumerate() {
- let CrateDep { name, extra_filename, hash, host_hash, kind } = dep;
+ let CrateDep { name, extra_filename, hash, host_hash, kind, is_private } = dep;
let number = i + 1;
writeln!(
out,
- "{number} {name}{extra_filename} hash {hash} host_hash {host_hash:?} kind {kind:?}"
+ "{number} {name}{extra_filename} hash {hash} host_hash {host_hash:?} kind {kind:?} {privacy}",
+ privacy = if is_private { "private" } else { "public" }
)?;
}
write!(out, "\n")?;
@@ -709,21 +723,17 @@ impl CrateRoot {
}
pub(crate) fn name(&self) -> Symbol {
- self.name
+ self.header.name
}
pub(crate) fn hash(&self) -> Svh {
- self.hash
+ self.header.hash
}
pub(crate) fn stable_crate_id(&self) -> StableCrateId {
self.stable_crate_id
}
- pub(crate) fn triple(&self) -> &TargetTriple {
- &self.triple
- }
-
pub(crate) fn decode_crate_deps<'a>(
&self,
metadata: &'a MetadataBlob,
@@ -794,7 +804,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
bug!(
"CrateMetadata::def_kind({:?}): id not found, in crate {:?} with number {}",
item_id,
- self.root.name,
+ self.root.name(),
self.cnum,
)
})
@@ -844,14 +854,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
self,
index: DefIndex,
tcx: TyCtxt<'tcx>,
- ) -> ty::EarlyBinder<&'tcx [(Predicate<'tcx>, Span)]> {
+ ) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> {
let lazy = self.root.tables.explicit_item_bounds.get(self, index);
let output = if lazy.is_default() {
&mut []
} else {
tcx.arena.alloc_from_iter(lazy.decode((self, tcx)))
};
- ty::EarlyBinder(&*output)
+ ty::EarlyBinder::bind(&*output)
}
fn get_variant(
@@ -985,6 +995,15 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
)
}
+ fn get_stripped_cfg_items(self, cnum: CrateNum, tcx: TyCtxt<'tcx>) -> &'tcx [StrippedCfgItem] {
+ let item_names = self
+ .root
+ .stripped_cfg_items
+ .decode((self, tcx))
+ .map(|item| item.map_mod_id(|index| DefId { krate: cnum, index }));
+ tcx.arena.alloc_from_iter(item_names)
+ }
+
/// Iterates over the diagnostic items in the given crate.
fn get_diagnostic_items(self) -> DiagnosticItems {
let mut id_to_name = FxHashMap::default();
@@ -1617,7 +1636,7 @@ impl CrateMetadata {
dependencies,
dep_kind: Lock::new(dep_kind),
source: Lrc::new(source),
- private_dep,
+ private_dep: AtomicBool::new(private_dep),
host_hash,
extern_crate: Lock::new(None),
hygiene_context: Default::default(),
@@ -1665,6 +1684,10 @@ impl CrateMetadata {
self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind))
}
+ pub(crate) fn update_and_private_dep(&self, private_dep: bool) {
+ self.private_dep.fetch_and(private_dep, Ordering::SeqCst);
+ }
+
pub(crate) fn required_panic_strategy(&self) -> Option<PanicStrategy> {
self.root.required_panic_strategy
}
@@ -1702,11 +1725,11 @@ impl CrateMetadata {
}
pub(crate) fn name(&self) -> Symbol {
- self.root.name
+ self.root.header.name
}
pub(crate) fn hash(&self) -> Svh {
- self.root.hash
+ self.root.header.hash
}
fn num_def_ids(&self) -> usize {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 7425963d3..848535fb3 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -218,6 +218,7 @@ provide! { tcx, def_id, other, cdata,
thir_abstract_const => { table }
optimized_mir => { table }
mir_for_ctfe => { table }
+ closure_saved_names_of_captured_variables => { table }
mir_generator_witnesses => { table }
promoted_mir => { table }
def_span => { table }
@@ -231,7 +232,7 @@ provide! { tcx, def_id, other, cdata,
opt_def_kind => { table_direct }
impl_parent => { table }
impl_polarity => { table_direct }
- impl_defaultness => { table_direct }
+ defaultness => { table_direct }
constness => { table_direct }
coerce_unsized_info => { table }
mir_const_qualif => { table }
@@ -285,7 +286,13 @@ provide! { tcx, def_id, other, cdata,
is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
- is_private_dep => { cdata.private_dep }
+ is_private_dep => {
+ // Parallel compiler needs to synchronize type checking and linting (which use this flag)
+ // so that they happen strictly crate loading. Otherwise, the full list of available
+ // impls aren't loaded yet.
+ use std::sync::atomic::Ordering;
+ cdata.private_dep.load(Ordering::Acquire)
+ }
is_panic_runtime => { cdata.root.panic_runtime }
is_compiler_builtins => { cdata.root.compiler_builtins }
has_global_allocator => { cdata.root.has_global_allocator }
@@ -317,9 +324,9 @@ provide! { tcx, def_id, other, cdata,
}
native_libraries => { cdata.get_native_libraries(tcx.sess).collect() }
foreign_modules => { cdata.get_foreign_modules(tcx.sess).map(|m| (m.def_id, m)).collect() }
- crate_hash => { cdata.root.hash }
+ crate_hash => { cdata.root.header.hash }
crate_host_hash => { cdata.host_hash }
- crate_name => { cdata.root.name }
+ crate_name => { cdata.root.header.name }
extra_filename => { cdata.root.extra_filename.clone() }
@@ -339,6 +346,7 @@ provide! { tcx, def_id, other, cdata,
stability_implications => {
cdata.get_stability_implications(tcx).iter().copied().collect()
}
+ stripped_cfg_items => { cdata.get_stripped_cfg_items(cdata.cnum, tcx) }
is_intrinsic => { cdata.get_is_intrinsic(def_id.index) }
defined_lang_items => { cdata.get_lang_items(tcx) }
diagnostic_items => { cdata.get_diagnostic_items() }
@@ -581,7 +589,7 @@ impl CrateStore for CStore {
}
fn crate_name(&self, cnum: CrateNum) -> Symbol {
- self.get_crate_data(cnum).root.name
+ self.get_crate_data(cnum).root.header.name
}
fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index f067bca4b..541c19c35 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
use crate::rmeta::table::TableBuilder;
use crate::rmeta::*;
+use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::Attribute;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@@ -16,9 +17,8 @@ use rustc_hir::def_id::{
CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE,
};
use rustc_hir::definitions::DefPathData;
-use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::intravisit;
use rustc_hir::lang_items::LangItem;
-use rustc_middle::hir::nested_filter;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{
@@ -30,7 +30,7 @@ use rustc_middle::query::Providers;
use rustc_middle::traits::specialization_graph;
use rustc_middle::ty::codec::TyEncoder;
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
-use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
+use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt};
use rustc_middle::util::common::to_readable_str;
use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder};
use rustc_session::config::{CrateType, OptLevel};
@@ -449,18 +449,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
LazyArray::from_position_and_num_elems(pos, len)
}
- fn encode_info_for_items(&mut self) {
- self.encode_info_for_mod(CRATE_DEF_ID);
-
- // Proc-macro crates only export proc-macro items, which are looked
- // up using `proc_macro_data`
- if self.is_proc_macro {
- return;
- }
-
- self.tcx.hir().visit_all_item_likes_in_crate(self);
- }
-
fn encode_def_path_table(&mut self) {
let table = self.tcx.def_path_table();
if self.is_proc_macro {
@@ -584,6 +572,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
(self.encode_lang_items(), self.encode_lang_items_missing())
});
+ let stripped_cfg_items = stat!("stripped-cfg-items", || self.encode_stripped_cfg_items());
+
let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items());
let native_libraries = stat!("native-libs", || self.encode_native_libraries());
@@ -604,8 +594,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
_ = stat!("def-ids", || self.encode_def_ids());
- _ = stat!("items", || self.encode_info_for_items());
-
let interpret_alloc_index = stat!("interpret-alloc-index", || {
let mut interpret_alloc_index = Vec::new();
let mut n = 0;
@@ -662,10 +650,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let root = stat!("final", || {
let attrs = tcx.hir().krate_attrs();
self.lazy(CrateRoot {
- name: tcx.crate_name(LOCAL_CRATE),
+ header: CrateHeader {
+ name: tcx.crate_name(LOCAL_CRATE),
+ triple: tcx.sess.opts.target_triple.clone(),
+ hash: tcx.crate_hash(LOCAL_CRATE),
+ is_proc_macro_crate: proc_macro_data.is_some(),
+ },
extra_filename: tcx.sess.opts.cg.extra_filename.clone(),
- triple: tcx.sess.opts.target_triple.clone(),
- hash: tcx.crate_hash(LOCAL_CRATE),
stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(),
required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE),
panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop,
@@ -691,6 +682,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
lang_items,
diagnostic_items,
lang_items_missing,
+ stripped_cfg_items,
native_libraries,
foreign_modules,
source_map,
@@ -1131,8 +1123,8 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
| DefKind::InlineConst => true,
DefKind::OpaqueTy => {
- let opaque = tcx.hir().expect_item(def_id).expect_opaque_ty();
- if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = opaque.origin
+ let origin = tcx.opaque_type_origin(def_id);
+ if let hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
&& let hir::Node::TraitItem(trait_item) = tcx.hir().get_by_def_id(fn_def_id)
&& let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn()
{
@@ -1159,10 +1151,10 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
let assoc_item = tcx.associated_item(def_id);
match assoc_item.container {
ty::AssocItemContainer::ImplContainer => true,
- // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) always encode RPITITs,
- // since we need to be able to "project" from an RPITIT associated item
- // to an opaque when installing the default projection predicates in
- // default trait methods with RPITITs.
+ // Always encode RPITITs, since we need to be able to project
+ // from an RPITIT associated item to an opaque when installing
+ // the default projection predicates in default trait methods
+ // with RPITITs.
ty::AssocItemContainer::TraitContainer => {
assoc_item.defaultness(tcx).has_value() || assoc_item.opt_rpitit_info.is_some()
}
@@ -1186,9 +1178,85 @@ fn should_encode_type(tcx: TyCtxt<'_>, def_id: LocalDefId, def_kind: DefKind) ->
}
}
+fn should_encode_fn_sig(def_kind: DefKind) -> bool {
+ match def_kind {
+ DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) => true,
+
+ DefKind::Struct
+ | DefKind::Union
+ | DefKind::Enum
+ | DefKind::Variant
+ | DefKind::Field
+ | DefKind::Const
+ | DefKind::Static(..)
+ | DefKind::Ctor(..)
+ | DefKind::TyAlias
+ | DefKind::OpaqueTy
+ | DefKind::ImplTraitPlaceholder
+ | DefKind::ForeignTy
+ | DefKind::Impl { .. }
+ | DefKind::AssocConst
+ | DefKind::Closure
+ | DefKind::Generator
+ | DefKind::ConstParam
+ | DefKind::AnonConst
+ | DefKind::InlineConst
+ | DefKind::AssocTy
+ | DefKind::TyParam
+ | DefKind::Trait
+ | DefKind::TraitAlias
+ | DefKind::Mod
+ | DefKind::ForeignMod
+ | DefKind::Macro(..)
+ | DefKind::Use
+ | DefKind::LifetimeParam
+ | DefKind::GlobalAsm
+ | DefKind::ExternCrate => false,
+ }
+}
+
+fn should_encode_constness(def_kind: DefKind) -> bool {
+ match def_kind {
+ DefKind::Fn
+ | DefKind::AssocFn
+ | DefKind::Closure
+ | DefKind::Impl { of_trait: true }
+ | DefKind::Variant
+ | DefKind::Ctor(..) => true,
+
+ DefKind::Struct
+ | DefKind::Union
+ | DefKind::Enum
+ | DefKind::Field
+ | DefKind::Const
+ | DefKind::AssocConst
+ | DefKind::AnonConst
+ | DefKind::Static(..)
+ | DefKind::TyAlias
+ | DefKind::OpaqueTy
+ | DefKind::Impl { of_trait: false }
+ | DefKind::ImplTraitPlaceholder
+ | DefKind::ForeignTy
+ | DefKind::Generator
+ | DefKind::ConstParam
+ | DefKind::InlineConst
+ | DefKind::AssocTy
+ | DefKind::TyParam
+ | DefKind::Trait
+ | DefKind::TraitAlias
+ | DefKind::Mod
+ | DefKind::ForeignMod
+ | DefKind::Macro(..)
+ | DefKind::Use
+ | DefKind::LifetimeParam
+ | DefKind::GlobalAsm
+ | DefKind::ExternCrate => false,
+ }
+}
+
fn should_encode_const(def_kind: DefKind) -> bool {
match def_kind {
- DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => true,
+ DefKind::Const | DefKind::AssocConst | DefKind::AnonConst | DefKind::InlineConst => true,
DefKind::Struct
| DefKind::Union
@@ -1207,7 +1275,6 @@ fn should_encode_const(def_kind: DefKind) -> bool {
| DefKind::Closure
| DefKind::Generator
| DefKind::ConstParam
- | DefKind::InlineConst
| DefKind::AssocTy
| DefKind::TyParam
| DefKind::Trait
@@ -1259,10 +1326,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
fn encode_def_ids(&mut self) {
+ self.encode_info_for_mod(CRATE_DEF_ID);
+
+ // Proc-macro crates only export proc-macro items, which are looked
+ // up using `proc_macro_data`
if self.is_proc_macro {
return;
}
+
let tcx = self.tcx;
+
for local_id in tcx.iter_local_def_id() {
let def_id = local_id.to_def_id();
let def_kind = tcx.opt_def_kind(local_id);
@@ -1299,30 +1372,84 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
let v = self.tcx.variances_of(def_id);
record_array!(self.tables.variances_of[def_id] <- v);
}
+ if should_encode_fn_sig(def_kind) {
+ record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
+ }
if should_encode_generics(def_kind) {
let g = tcx.generics_of(def_id);
record!(self.tables.generics_of[def_id] <- g);
record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id));
let inferred_outlives = self.tcx.inferred_outlives_of(def_id);
record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
+
+ for param in &g.params {
+ if let ty::GenericParamDefKind::Const { has_default: true, .. } = param.kind {
+ let default = self.tcx.const_param_default(param.def_id);
+ record!(self.tables.const_param_default[param.def_id] <- default);
+ }
+ }
}
if should_encode_type(tcx, local_id, def_kind) {
record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
}
+ if should_encode_constness(def_kind) {
+ self.tables.constness.set_some(def_id.index, self.tcx.constness(def_id));
+ }
+ if let DefKind::Fn | DefKind::AssocFn = def_kind {
+ self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
+ record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
+ self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
+ }
if let DefKind::TyParam = def_kind {
let default = self.tcx.object_lifetime_default(def_id);
record!(self.tables.object_lifetime_default[def_id] <- default);
}
if let DefKind::Trait = def_kind {
+ record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
+
+ let module_children = self.tcx.module_children_local(local_id);
+ record_array!(self.tables.module_children_non_reexports[def_id] <-
+ module_children.iter().map(|child| child.res.def_id().index));
}
if let DefKind::TraitAlias = def_kind {
+ record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id));
}
+ if let DefKind::Trait | DefKind::Impl { .. } = def_kind {
+ let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
+ record_array!(self.tables.associated_item_or_field_def_ids[def_id] <-
+ associated_item_def_ids.iter().map(|&def_id| {
+ assert!(def_id.is_local());
+ def_id.index
+ })
+ );
+ for &def_id in associated_item_def_ids {
+ self.encode_info_for_assoc_item(def_id);
+ }
+ }
+ if let DefKind::Generator = def_kind {
+ self.encode_info_for_generator(local_id);
+ }
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
self.encode_info_for_adt(local_id);
}
+ if let DefKind::Mod = def_kind {
+ self.encode_info_for_mod(local_id);
+ }
+ if let DefKind::Macro(_) = def_kind {
+ self.encode_info_for_macro(local_id);
+ }
+ if let DefKind::OpaqueTy = def_kind {
+ self.encode_explicit_item_bounds(def_id);
+ self.tables
+ .is_type_alias_impl_trait
+ .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id));
+ }
+ if let DefKind::ImplTraitPlaceholder = def_kind {
+ self.encode_explicit_item_bounds(def_id);
+ }
if tcx.impl_method_has_trait_impl_trait_tys(def_id)
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
{
@@ -1383,26 +1510,23 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
};
record!(self.tables.variant_data[variant.def_id] <- data);
- self.tables.constness.set_some(variant.def_id.index, hir::Constness::Const);
record_array!(self.tables.associated_item_or_field_def_ids[variant.def_id] <- variant.fields.iter().map(|f| {
assert!(f.did.is_local());
f.did.index
}));
if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
- self.tables.constness.set_some(ctor_def_id.index, hir::Constness::Const);
let fn_sig = tcx.fn_sig(ctor_def_id);
- record!(self.tables.fn_sig[ctor_def_id] <- fn_sig);
// FIXME only encode signature for ctor_def_id
record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
}
}
}
+ #[instrument(level = "debug", skip(self))]
fn encode_info_for_mod(&mut self, local_def_id: LocalDefId) {
let tcx = self.tcx;
let def_id = local_def_id.to_def_id();
- debug!("EncodeContext::encode_info_for_mod({:?})", def_id);
// If we are encoding a proc-macro crates, `encode_info_for_mod` will
// only ever get called for the crate root. We still want to encode
@@ -1430,70 +1554,28 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
record_defaulted_array!(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);
+ #[instrument(level = "debug", skip(self))]
+ fn encode_info_for_assoc_item(&mut self, def_id: DefId) {
let tcx = self.tcx;
+ let item = tcx.associated_item(def_id);
- let impl_defaultness = tcx.impl_defaultness(def_id.expect_local());
- self.tables.impl_defaultness.set_some(def_id.index, impl_defaultness);
- let trait_item = tcx.associated_item(def_id);
- self.tables.assoc_container.set_some(def_id.index, trait_item.container);
+ self.tables.defaultness.set_some(def_id.index, item.defaultness(tcx));
+ self.tables.assoc_container.set_some(def_id.index, item.container);
- match trait_item.kind {
- ty::AssocKind::Const => {}
- ty::AssocKind::Fn => {
- record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
- self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
- self.tables.constness.set_some(def_id.index, hir::Constness::NotConst);
- }
- ty::AssocKind::Type => {
- self.encode_explicit_item_bounds(def_id);
+ match item.container {
+ AssocItemContainer::TraitContainer => {
+ if let ty::AssocKind::Type = item.kind {
+ self.encode_explicit_item_bounds(def_id);
+ }
}
- }
- if trait_item.kind == ty::AssocKind::Fn {
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- }
- if let Some(rpitit_info) = trait_item.opt_rpitit_info {
- let rpitit_info = self.lazy(rpitit_info);
- self.tables.opt_rpitit_info.set_some(def_id.index, rpitit_info);
- }
- }
-
- fn encode_info_for_impl_item(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_info_for_impl_item({:?})", def_id);
- let tcx = self.tcx;
-
- let defaultness = self.tcx.impl_defaultness(def_id.expect_local());
- self.tables.impl_defaultness.set_some(def_id.index, defaultness);
- let impl_item = self.tcx.associated_item(def_id);
- self.tables.assoc_container.set_some(def_id.index, impl_item.container);
-
- match impl_item.kind {
- ty::AssocKind::Fn => {
- let (sig, body) =
- self.tcx.hir().expect_impl_item(def_id.expect_local()).expect_fn();
- self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
- record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
- // Can be inside `impl const Trait`, so using sig.header.constness is not reliable
- let constness = if self.tcx.is_const_fn_raw(def_id) {
- hir::Constness::Const
- } else {
- hir::Constness::NotConst
- };
- self.tables.constness.set_some(def_id.index, constness);
+ AssocItemContainer::ImplContainer => {
+ if let Some(trait_item_def_id) = item.trait_item_def_id {
+ self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into());
+ }
}
- ty::AssocKind::Const | ty::AssocKind::Type => {}
- }
- if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
- self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into());
- }
- if impl_item.kind == ty::AssocKind::Fn {
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
}
- if let Some(rpitit_info) = impl_item.opt_rpitit_info {
- let rpitit_info = self.lazy(rpitit_info);
- self.tables.opt_rpitit_info.set_some(def_id.index, rpitit_info);
+ if let Some(rpitit_info) = item.opt_rpitit_info {
+ record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info);
}
}
@@ -1514,6 +1596,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
debug!("EntryBuilder::encode_mir({:?})", def_id);
if encode_opt {
record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
+ record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()]
+ <- tcx.closure_saved_names_of_captured_variables(def_id));
if tcx.sess.opts.unstable_opts.drop_tracking_mir
&& let DefKind::Generator = self.tcx.def_kind(def_id)
@@ -1564,9 +1648,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
+ #[instrument(level = "debug", skip(self))]
fn encode_stability(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_stability({:?})", def_id);
-
// The query lookup can take a measurable amount of time in crates with many items. Check if
// the stability attributes are even enabled before using their queries.
if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
@@ -1576,9 +1659,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
+ #[instrument(level = "debug", skip(self))]
fn encode_const_stability(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_const_stability({:?})", def_id);
-
// The query lookup can take a measurable amount of time in crates with many items. Check if
// the stability attributes are even enabled before using their queries.
if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
@@ -1588,9 +1670,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
+ #[instrument(level = "debug", skip(self))]
fn encode_default_body_stability(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_default_body_stability({:?})", def_id);
-
// The query lookup can take a measurable amount of time in crates with many items. Check if
// the stability attributes are even enabled before using their queries.
if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
@@ -1600,8 +1681,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
}
+ #[instrument(level = "debug", skip(self))]
fn encode_deprecation(&mut self, def_id: DefId) {
- debug!("EncodeContext::encode_deprecation({:?})", def_id);
if let Some(depr) = self.tcx.lookup_deprecation(def_id) {
record!(self.tables.lookup_deprecation_entry[def_id] <- depr);
}
@@ -1615,123 +1696,22 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
})
}
- fn encode_info_for_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ #[instrument(level = "debug", skip(self))]
+ fn encode_info_for_macro(&mut self, def_id: LocalDefId) {
let tcx = self.tcx;
- let def_id = item.owner_id.to_def_id();
- debug!("EncodeContext::encode_info_for_item({:?})", def_id);
-
- let record_associated_item_def_ids = |this: &mut Self, def_ids: &[DefId]| {
- record_array!(this.tables.associated_item_or_field_def_ids[def_id] <- def_ids.iter().map(|&def_id| {
- assert!(def_id.is_local());
- def_id.index
- }))
- };
-
- match item.kind {
- hir::ItemKind::Fn(ref sig, .., body) => {
- self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
- record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
- self.tables.constness.set_some(def_id.index, sig.header.constness);
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
- }
- hir::ItemKind::Macro(ref macro_def, _) => {
- self.tables.is_macro_rules.set(def_id.index, macro_def.macro_rules);
- record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
- }
- hir::ItemKind::Mod(..) => {
- self.encode_info_for_mod(item.owner_id.def_id);
- }
- hir::ItemKind::OpaqueTy(ref opaque) => {
- self.encode_explicit_item_bounds(def_id);
- self.tables.is_type_alias_impl_trait.set(
- def_id.index,
- matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias { .. }),
- );
- }
- hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
- self.tables.impl_defaultness.set_some(def_id.index, *defaultness);
- self.tables.constness.set_some(def_id.index, *constness);
- self.tables.impl_polarity.set_some(def_id.index, self.tcx.impl_polarity(def_id));
-
- if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) {
- record!(self.tables.impl_trait_ref[def_id] <- trait_ref);
-
- let trait_ref = trait_ref.skip_binder();
- let trait_def = self.tcx.trait_def(trait_ref.def_id);
- if let Ok(mut an) = trait_def.ancestors(self.tcx, def_id) {
- if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) {
- self.tables.impl_parent.set_some(def_id.index, parent.into());
- }
- }
-
- // if this is an impl of `CoerceUnsized`, create its
- // "unsized info", else just store None
- if Some(trait_ref.def_id) == self.tcx.lang_items().coerce_unsized_trait() {
- let coerce_unsized_info =
- self.tcx.at(item.span).coerce_unsized_info(def_id);
- record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
- }
- }
-
- let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
- record_associated_item_def_ids(self, associated_item_def_ids);
- for &trait_item_def_id in associated_item_def_ids {
- self.encode_info_for_impl_item(trait_item_def_id);
- }
- }
- hir::ItemKind::Trait(..) => {
- record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
- let module_children = tcx.module_children_local(item.owner_id.def_id);
- record_array!(self.tables.module_children_non_reexports[def_id] <-
- module_children.iter().map(|child| child.res.def_id().index));
-
- let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
- record_associated_item_def_ids(self, associated_item_def_ids);
- for &item_def_id in associated_item_def_ids {
- self.encode_info_for_trait_item(item_def_id);
- }
- }
- hir::ItemKind::TraitAlias(..) => {
- record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
- }
- hir::ItemKind::ExternCrate(_)
- | hir::ItemKind::Use(..)
- | hir::ItemKind::Static(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Enum(..)
- | hir::ItemKind::Struct(..)
- | hir::ItemKind::Union(..)
- | hir::ItemKind::ForeignMod { .. }
- | hir::ItemKind::GlobalAsm(..)
- | hir::ItemKind::TyAlias(..) => {}
- }
+ let hir::ItemKind::Macro(ref macro_def, _) = tcx.hir().expect_item(def_id).kind else { bug!() };
+ self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules);
+ record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body);
}
#[instrument(level = "debug", skip(self))]
- fn encode_info_for_closure(&mut self, def_id: LocalDefId) {
- // NOTE(eddyb) `tcx.type_of(def_id)` isn't used because it's fully generic,
- // including on the signature, which is inferred in `typeck`.
+ fn encode_info_for_generator(&mut self, def_id: LocalDefId) {
let typeck_result: &'tcx ty::TypeckResults<'tcx> = self.tcx.typeck(def_id);
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- let ty = typeck_result.node_type(hir_id);
- match ty.kind() {
- ty::Generator(..) => {
- let data = self.tcx.generator_kind(def_id).unwrap();
- let generator_diagnostic_data = typeck_result.get_generator_diagnostic_data();
- record!(self.tables.generator_kind[def_id.to_def_id()] <- data);
- record!(self.tables.generator_diagnostic_data[def_id.to_def_id()] <- generator_diagnostic_data);
- }
-
- ty::Closure(_, substs) => {
- let constness = self.tcx.constness(def_id.to_def_id());
- self.tables.constness.set_some(def_id.to_def_id().index, constness);
- record!(self.tables.fn_sig[def_id.to_def_id()] <- ty::EarlyBinder(substs.as_closure().sig()));
- }
-
- _ => bug!("closure that is neither generator nor closure"),
- }
+ let data = self.tcx.generator_kind(def_id).unwrap();
+ let generator_diagnostic_data = typeck_result.get_generator_diagnostic_data();
+ record!(self.tables.generator_kind[def_id.to_def_id()] <- data);
+ record!(self.tables.generator_diagnostic_data[def_id.to_def_id()] <- generator_diagnostic_data);
}
fn encode_native_libraries(&mut self) -> LazyArray<NativeLib> {
@@ -1880,6 +1860,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
host_hash: self.tcx.crate_host_hash(cnum),
kind: self.tcx.dep_kind(cnum),
extra_filename: self.tcx.extra_filename(cnum).clone(),
+ is_private: self.tcx.is_private_dep(cnum),
};
(cnum, dep)
})
@@ -1936,34 +1917,58 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_array(&tcx.lang_items().missing)
}
+ fn encode_stripped_cfg_items(&mut self) -> LazyArray<StrippedCfgItem<DefIndex>> {
+ self.lazy_array(
+ self.tcx
+ .stripped_cfg_items(LOCAL_CRATE)
+ .into_iter()
+ .map(|item| item.clone().map_mod_id(|def_id| def_id.index)),
+ )
+ }
+
fn encode_traits(&mut self) -> LazyArray<DefIndex> {
empty_proc_macro!(self);
self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index))
}
/// Encodes an index, mapping each trait to its (local) implementations.
+ #[instrument(level = "debug", skip(self))]
fn encode_impls(&mut self) -> LazyArray<TraitImpls> {
- debug!("EncodeContext::encode_traits_and_impls()");
empty_proc_macro!(self);
let tcx = self.tcx;
let mut fx_hash_map: FxHashMap<DefId, Vec<(DefIndex, Option<SimplifiedType>)>> =
FxHashMap::default();
for id in tcx.hir().items() {
- if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) {
- if let Some(trait_ref) = tcx.impl_trait_ref(id.owner_id) {
- let trait_ref = trait_ref.subst_identity();
-
- let simplified_self_ty = fast_reject::simplify_type(
- self.tcx,
- trait_ref.self_ty(),
- TreatParams::AsCandidateKey,
- );
-
- fx_hash_map
- .entry(trait_ref.def_id)
- .or_default()
- .push((id.owner_id.def_id.local_def_index, simplified_self_ty));
+ let DefKind::Impl { of_trait } = tcx.def_kind(id.owner_id) else { continue; };
+ let def_id = id.owner_id.to_def_id();
+
+ self.tables.defaultness.set_some(def_id.index, tcx.defaultness(def_id));
+ self.tables.impl_polarity.set_some(def_id.index, tcx.impl_polarity(def_id));
+
+ if of_trait && let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
+ record!(self.tables.impl_trait_ref[def_id] <- trait_ref);
+
+ let trait_ref = trait_ref.subst_identity();
+ let simplified_self_ty =
+ fast_reject::simplify_type(self.tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey);
+ fx_hash_map
+ .entry(trait_ref.def_id)
+ .or_default()
+ .push((id.owner_id.def_id.local_def_index, simplified_self_ty));
+
+ let trait_def = tcx.trait_def(trait_ref.def_id);
+ if let Some(mut an) = trait_def.ancestors(tcx, def_id).ok() {
+ if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) {
+ self.tables.impl_parent.set_some(def_id.index, parent.into());
+ }
+ }
+
+ // if this is an impl of `CoerceUnsized`, create its
+ // "unsized info", else just store None
+ if Some(trait_ref.def_id) == tcx.lang_items().coerce_unsized_trait() {
+ let coerce_unsized_info = tcx.coerce_unsized_info(def_id);
+ record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
}
}
}
@@ -1991,8 +1996,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
self.lazy_array(&all_impls)
}
+ #[instrument(level = "debug", skip(self))]
fn encode_incoherent_impls(&mut self) -> LazyArray<IncoherentImpls> {
- debug!("EncodeContext::encode_traits_and_impls()");
empty_proc_macro!(self);
let tcx = self.tcx;
let mut all_impls: Vec<_> = tcx.crate_inherent_impls(()).incoherent_impls.iter().collect();
@@ -2061,77 +2066,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
}
LazyArray::default()
}
-
- fn encode_info_for_foreign_item(&mut self, def_id: DefId, nitem: &hir::ForeignItem<'_>) {
- let tcx = self.tcx;
-
- debug!("EncodeContext::encode_info_for_foreign_item({:?})", def_id);
-
- match nitem.kind {
- hir::ForeignItemKind::Fn(_, ref names, _) => {
- self.tables.asyncness.set_some(def_id.index, hir::IsAsync::NotAsync);
- record_array!(self.tables.fn_arg_names[def_id] <- *names);
- let constness = if self.tcx.is_const_fn_raw(def_id) {
- hir::Constness::Const
- } else {
- hir::Constness::NotConst
- };
- self.tables.constness.set_some(def_id.index, constness);
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- }
- hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => {}
- }
- if let hir::ForeignItemKind::Fn(..) = nitem.kind {
- self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
- }
- }
-}
-
-// FIXME(eddyb) make metadata encoding walk over all definitions, instead of HIR.
-impl<'a, 'tcx> Visitor<'tcx> for EncodeContext<'a, 'tcx> {
- type NestedFilter = nested_filter::OnlyBodies;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
- fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
- intravisit::walk_expr(self, ex);
- self.encode_info_for_expr(ex);
- }
- fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
- intravisit::walk_item(self, item);
- self.encode_info_for_item(item);
- }
- fn visit_foreign_item(&mut self, ni: &'tcx hir::ForeignItem<'tcx>) {
- intravisit::walk_foreign_item(self, ni);
- self.encode_info_for_foreign_item(ni.owner_id.to_def_id(), ni);
- }
- fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
- intravisit::walk_generics(self, generics);
- self.encode_info_for_generics(generics);
- }
-}
-
-impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
- fn encode_info_for_generics(&mut self, generics: &hir::Generics<'tcx>) {
- for param in generics.params {
- match param.kind {
- hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => {}
- hir::GenericParamKind::Const { ref default, .. } => {
- let def_id = param.def_id.to_def_id();
- if default.is_some() {
- record!(self.tables.const_param_default[def_id] <- self.tcx.const_param_default(def_id))
- }
- }
- }
- }
- }
-
- fn encode_info_for_expr(&mut self, expr: &hir::Expr<'_>) {
- if let hir::ExprKind::Closure(closure) = expr.kind {
- self.encode_info_for_closure(closure.def_id);
- }
- }
}
/// Used to prefetch queries which will be needed later by metadata encoding.
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 97e67fcf8..9cffd96f4 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -6,6 +6,7 @@ use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use table::TableBuilder;
use rustc_ast as ast;
+use rustc_ast::expand::StrippedCfgItem;
use rustc_attr as attr;
use rustc_data_structures::svh::Svh;
use rustc_hir as hir;
@@ -31,7 +32,7 @@ use rustc_span::edition::Edition;
use rustc_span::hygiene::{ExpnIndex, MacroKind};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::{self, ExpnData, ExpnHash, ExpnId, Span};
-use rustc_target::abi::VariantIdx;
+use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_target::spec::{PanicStrategy, TargetTriple};
use std::marker::PhantomData;
@@ -56,7 +57,7 @@ pub(crate) fn rustc_version(cfg_version: &'static str) -> String {
/// Metadata encoding version.
/// N.B., increment this if you change the format of metadata such that
/// the rustc version can't be found to compare with `rustc_version()`.
-const METADATA_VERSION: u8 = 7;
+const METADATA_VERSION: u8 = 8;
/// Metadata header which includes `METADATA_VERSION`.
///
@@ -199,7 +200,27 @@ pub(crate) struct ProcMacroData {
macros: LazyArray<DefIndex>,
}
-/// Serialized metadata for a crate.
+/// Serialized crate metadata.
+///
+/// This contains just enough information to determine if we should load the `CrateRoot` or not.
+/// Prefer [`CrateRoot`] whenever possible to avoid ICEs when using `omit-git-hash` locally.
+/// See #76720 for more details.
+///
+/// If you do modify this struct, also bump the [`METADATA_VERSION`] constant.
+#[derive(MetadataEncodable, MetadataDecodable)]
+pub(crate) struct CrateHeader {
+ pub(crate) triple: TargetTriple,
+ pub(crate) hash: Svh,
+ pub(crate) name: Symbol,
+ /// Whether this is the header for a proc-macro crate.
+ ///
+ /// This is separate from [`ProcMacroData`] to avoid having to update [`METADATA_VERSION`] every
+ /// time ProcMacroData changes.
+ pub(crate) is_proc_macro_crate: bool,
+}
+
+/// Serialized `.rmeta` data for a crate.
+///
/// When compiling a proc-macro crate, we encode many of
/// the `LazyArray<T>` fields as `Lazy::empty()`. This serves two purposes:
///
@@ -217,10 +238,10 @@ pub(crate) struct ProcMacroData {
/// to being unused.
#[derive(MetadataEncodable, MetadataDecodable)]
pub(crate) struct CrateRoot {
- name: Symbol,
- triple: TargetTriple,
+ /// A header used to detect if this is the right crate to load.
+ header: CrateHeader,
+
extra_filename: String,
- hash: Svh,
stable_crate_id: StableCrateId,
required_panic_strategy: Option<PanicStrategy>,
panic_in_drop_strategy: PanicStrategy,
@@ -236,6 +257,7 @@ pub(crate) struct CrateRoot {
stability_implications: LazyArray<(Symbol, Symbol)>,
lang_items: LazyArray<(DefIndex, LangItem)>,
lang_items_missing: LazyArray<LangItem>,
+ stripped_cfg_items: LazyArray<StrippedCfgItem<DefIndex>>,
diagnostic_items: LazyArray<(Symbol, DefIndex)>,
native_libraries: LazyArray<NativeLib>,
foreign_modules: LazyArray<ForeignModule>,
@@ -302,6 +324,7 @@ pub(crate) struct CrateDep {
pub host_hash: Option<Svh>,
pub kind: CrateDepKind,
pub extra_filename: String,
+ pub is_private: bool,
}
#[derive(MetadataEncodable, MetadataDecodable)]
@@ -352,7 +375,7 @@ define_tables! {
is_type_alias_impl_trait: Table<DefIndex, bool>,
attr_flags: Table<DefIndex, AttrFlags>,
def_path_hashes: Table<DefIndex, DefPathHash>,
- explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
+ explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
@@ -393,13 +416,14 @@ define_tables! {
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
+ closure_saved_names_of_captured_variables: Table<DefIndex, LazyValue<IndexVec<FieldIdx, Symbol>>>,
mir_generator_witnesses: Table<DefIndex, LazyValue<mir::GeneratorLayout<'static>>>,
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
thir_abstract_const: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::Const<'static>>>>,
impl_parent: Table<DefIndex, RawDefId>,
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
constness: Table<DefIndex, hir::Constness>,
- impl_defaultness: Table<DefIndex, hir::Defaultness>,
+ defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
mir_const_qualif: Table<DefIndex, LazyValue<mir::ConstQualifs>>,
@@ -465,6 +489,7 @@ trivially_parameterized_over_tcx! {
RawDefId,
TraitImpls,
IncoherentImpls,
+ CrateHeader,
CrateRoot,
CrateDep,
AttrFlags,
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index dda30bce2..f002d7f97 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -439,7 +439,7 @@ where
/// Given the metadata, extract out the value at a particular index (if any).
#[inline(never)]
pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
- debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
+ trace!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
let start = self.position.get();
let bytes = &metadata.blob()[start..start + self.encoded_size];
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 7c56af1da..4c238308f 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
[dependencies]
bitflags = "1.2.1"
-chalk-ir = "0.87.0"
derive_more = "0.99.17"
either = "1.5.0"
gsgdt = "0.1.2"
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index 3d581daa9..bb7147ac8 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -1,3 +1,38 @@
+middle_adjust_for_foreign_abi_error =
+ target architecture {$arch} does not support `extern {$abi}` ABI
+
+middle_assert_async_resume_after_panic = `async fn` resumed after panicking
+
+middle_assert_async_resume_after_return = `async fn` resumed after completion
+
+middle_assert_divide_by_zero =
+ attempt to divide `{$val}` by zero
+
+middle_assert_generator_resume_after_panic = generator resumed after panicking
+
+middle_assert_generator_resume_after_return = generator resumed after completion
+
+middle_assert_misaligned_ptr_deref =
+ misaligned pointer dereference: address must be a multiple of {$required} but is {$found}
+
+middle_assert_op_overflow =
+ attempt to compute `{$left} {$op} {$right}`, which would overflow
+
+middle_assert_overflow_neg =
+ attempt to negate `{$val}`, which would overflow
+
+middle_assert_remainder_by_zero =
+ attempt to calculate the remainder of `{$val}` with a divisor of zero
+
+middle_assert_shl_overflow =
+ attempt to shift left by `{$val}`, which would overflow
+
+middle_assert_shr_overflow =
+ attempt to shift right by `{$val}`, which would overflow
+
+middle_bounds_check =
+ index out of bounds: the length is {$len} but the index is {$index}
+
middle_cannot_be_normalized =
unable to determine layout for `{$ty}` because `{$failure_ty}` cannot be normalized
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index a149a61ec..5a320865c 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -27,6 +27,11 @@ macro_rules! arena_types {
rustc_middle::mir::Promoted,
rustc_middle::mir::Body<'tcx>
>,
+ [decode] closure_debuginfo:
+ rustc_index::IndexVec<
+ rustc_target::abi::FieldIdx,
+ rustc_span::symbol::Symbol,
+ >,
[decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>,
[decode] borrowck_result:
rustc_middle::mir::BorrowCheckResult<'tcx>,
@@ -78,9 +83,9 @@ macro_rules! arena_types {
rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::FnSig<'tcx>>
>,
- [] type_op_normalize_predicate:
+ [] type_op_normalize_clause:
rustc_middle::infer::canonical::Canonical<'tcx,
- rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Predicate<'tcx>>
+ rustc_middle::infer::canonical::QueryResponse<'tcx, rustc_middle::ty::Clause<'tcx>>
>,
[] type_op_normalize_ty:
rustc_middle::infer::canonical::Canonical<'tcx,
@@ -124,6 +129,7 @@ macro_rules! arena_types {
[] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>,
[decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
[] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>),
+ [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem,
[] mod_child: rustc_middle::metadata::ModChild,
]);
)
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index 046186d27..57b2de84b 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -1,3 +1,7 @@
+use std::borrow::Cow;
+use std::fmt;
+
+use rustc_errors::{DiagnosticArgValue, DiagnosticMessage};
use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol};
@@ -88,3 +92,54 @@ pub(super) struct ConstNotUsedTraitAlias {
#[primary_span]
pub span: Span,
}
+
+pub struct CustomSubdiagnostic<'a> {
+ pub msg: fn() -> DiagnosticMessage,
+ pub add_args:
+ Box<dyn FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a>,
+}
+
+impl<'a> CustomSubdiagnostic<'a> {
+ pub fn label(x: fn() -> DiagnosticMessage) -> Self {
+ Self::label_and_then(x, |_| {})
+ }
+ pub fn label_and_then<
+ F: FnOnce(&mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>)) + 'a,
+ >(
+ msg: fn() -> DiagnosticMessage,
+ f: F,
+ ) -> Self {
+ Self { msg, add_args: Box::new(move |x| f(x)) }
+ }
+}
+
+impl fmt::Debug for CustomSubdiagnostic<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CustomSubdiagnostic").finish_non_exhaustive()
+ }
+}
+
+#[derive(Diagnostic)]
+pub enum LayoutError<'tcx> {
+ #[diag(middle_unknown_layout)]
+ Unknown { ty: Ty<'tcx> },
+
+ #[diag(middle_values_too_big)]
+ Overflow { ty: Ty<'tcx> },
+
+ #[diag(middle_cannot_be_normalized)]
+ NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },
+
+ #[diag(middle_cycle)]
+ Cycle,
+}
+
+#[derive(Diagnostic)]
+#[diag(middle_adjust_for_foreign_abi_error)]
+pub struct UnsupportedFnAbi {
+ pub arch: Symbol,
+ pub abi: &'static str,
+}
+
+/// Used by `rustc_const_eval`
+pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error;
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index d1ddc8fc1..5f2eb890c 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -44,6 +44,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> {
}
Node::AnonConst(constant) => Some((constant.def_id, constant.body)),
+ Node::ConstBlock(constant) => Some((constant.def_id, constant.body)),
_ => None,
}
@@ -240,15 +241,8 @@ impl<'hir> Map<'hir> {
None => bug!("constructor node without a constructor"),
}
}
- Node::AnonConst(_) => {
- let inline = match self.find_parent(hir_id) {
- Some(Node::Expr(&Expr {
- kind: ExprKind::ConstBlock(ref anon_const), ..
- })) if anon_const.hir_id == hir_id => true,
- _ => false,
- };
- if inline { DefKind::InlineConst } else { DefKind::AnonConst }
- }
+ Node::AnonConst(_) => DefKind::AnonConst,
+ Node::ConstBlock(_) => DefKind::InlineConst,
Node::Field(_) => DefKind::Field,
Node::Expr(expr) => match expr.kind {
ExprKind::Closure(Closure { movability: None, .. }) => DefKind::Closure,
@@ -1060,6 +1054,7 @@ impl<'hir> Map<'hir> {
Node::Variant(variant) => variant.span,
Node::Field(field) => field.span,
Node::AnonConst(constant) => self.body(constant.body).value.span,
+ Node::ConstBlock(constant) => self.body(constant.body).value.span,
Node::Expr(expr) => expr.span,
Node::ExprField(field) => field.span,
Node::Stmt(stmt) => stmt.span,
@@ -1289,6 +1284,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id))
}
Some(Node::AnonConst(_)) => node_str("const"),
+ Some(Node::ConstBlock(_)) => node_str("const"),
Some(Node::Expr(_)) => node_str("expr"),
Some(Node::ExprField(_)) => node_str("expr field"),
Some(Node::Stmt(_)) => node_str("stmt"),
@@ -1434,6 +1430,11 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
intravisit::walk_anon_const(self, c)
}
+ fn visit_inline_const(&mut self, c: &'hir ConstBlock) {
+ self.body_owners.push(c.def_id);
+ intravisit::walk_inline_const(self, c)
+ }
+
fn visit_expr(&mut self, ex: &'hir Expr<'hir>) {
if let ExprKind::Closure(closure) = ex.kind {
self.body_owners.push(closure.def_id);
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index 561713149..d5e8330b3 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -82,15 +82,40 @@ impl CanonicalVarValues<'_> {
}
pub fn is_identity_modulo_regions(&self) -> bool {
- self.var_values.iter().enumerate().all(|(bv, arg)| match arg.unpack() {
- ty::GenericArgKind::Lifetime(_) => true,
- ty::GenericArgKind::Type(ty) => {
- matches!(*ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var.as_usize() == bv)
- }
- ty::GenericArgKind::Const(ct) => {
- matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.as_usize() == bv)
+ let mut var = ty::BoundVar::from_u32(0);
+ for arg in self.var_values {
+ match arg.unpack() {
+ ty::GenericArgKind::Lifetime(r) => {
+ if let ty::ReLateBound(ty::INNERMOST, br) = *r
+ && var == br.var
+ {
+ var = var + 1;
+ } else {
+ // It's ok if this region var isn't unique
+ }
+ },
+ ty::GenericArgKind::Type(ty) => {
+ if let ty::Bound(ty::INNERMOST, bt) = *ty.kind()
+ && var == bt.var
+ {
+ var = var + 1;
+ } else {
+ return false;
+ }
+ }
+ ty::GenericArgKind::Const(ct) => {
+ if let ty::ConstKind::Bound(ty::INNERMOST, bc) = ct.kind()
+ && var == bc
+ {
+ var = var + 1;
+ } else {
+ return false;
+ }
+ }
}
- })
+ }
+
+ true
}
}
@@ -408,22 +433,24 @@ impl<'tcx> CanonicalVarValues<'tcx> {
|(i, info)| -> ty::GenericArg<'tcx> {
match info.kind {
CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => {
- tcx.mk_bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into()).into()
+ Ty::new_bound(tcx, ty::INNERMOST, ty::BoundVar::from_usize(i).into())
+ .into()
}
CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(i),
kind: ty::BrAnon(None),
};
- tcx.mk_re_late_bound(ty::INNERMOST, br).into()
+ ty::Region::new_late_bound(tcx, ty::INNERMOST, br).into()
}
CanonicalVarKind::Const(_, ty)
- | CanonicalVarKind::PlaceholderConst(_, ty) => tcx
- .mk_const(
- ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i)),
- ty,
- )
- .into(),
+ | CanonicalVarKind::PlaceholderConst(_, ty) => ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(i),
+ ty,
+ )
+ .into(),
}
},
)),
diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs
index a873854f0..85fb9214d 100644
--- a/compiler/rustc_middle/src/infer/unify_key.rs
+++ b/compiler/rustc_middle/src/infer/unify_key.rs
@@ -90,15 +90,15 @@ impl<'tcx> UnifyValue for UnifiedRegion<'tcx> {
impl ToType for ty::IntVarValue {
fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {
- ty::IntType(i) => tcx.mk_mach_int(i),
- ty::UintType(i) => tcx.mk_mach_uint(i),
+ ty::IntType(i) => Ty::new_int(tcx, i),
+ ty::UintType(i) => Ty::new_uint(tcx, i),
}
}
}
impl ToType for ty::FloatVarValue {
fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
- tcx.mk_mach_float(self.0)
+ Ty::new_float(tcx, self.0)
}
}
@@ -116,7 +116,6 @@ pub enum ConstVariableOriginKind {
MiscVariable,
ConstInference,
ConstParameterDefinition(Symbol, DefId),
- SubstitutionPlaceholder,
}
#[derive(Copy, Clone, Debug)]
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index 22ee2a8c5..1b125e8e2 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -48,12 +48,13 @@
#![feature(associated_type_bounds)]
#![feature(rustc_attrs)]
#![feature(control_flow_enum)]
+#![feature(trait_upcasting)]
#![feature(trusted_step)]
#![feature(try_blocks)]
#![feature(try_reserve_kind)]
#![feature(nonzero_ops)]
#![feature(decl_macro)]
-#![feature(drain_filter)]
+#![feature(extract_if)]
#![feature(intra_doc_pointers)]
#![feature(yeet_expr)]
#![feature(result_option_inspect)]
@@ -86,7 +87,7 @@ mod macros;
#[macro_use]
pub mod arena;
-pub(crate) mod error;
+pub mod error;
pub mod hir;
pub mod infer;
pub mod lint;
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 14343ac11..81c1ae4f6 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -251,7 +251,7 @@ pub fn explain_lint_level_source(
}
LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
if let Some(rationale) = reason {
- err.note(rationale.as_str());
+ err.note(rationale.to_string());
}
err.span_note_once(span, "the lint level is defined here");
if lint_attr_name.as_str() != name {
@@ -388,10 +388,11 @@ pub fn struct_lint_level(
// it'll become a hard error, so we have to emit *something*. Also,
// if this lint occurs in the expansion of a macro from an external crate,
// allow individual lints to opt-out from being reported.
- let not_future_incompatible =
- future_incompatible.map(|f| f.reason.edition().is_some()).unwrap_or(true);
- if not_future_incompatible && !lint.report_in_external_macro {
+ let incompatible = future_incompatible.is_some_and(|f| f.reason.edition().is_none());
+
+ if !incompatible && !lint.report_in_external_macro {
err.cancel();
+
// Don't continue further, since we don't want to have
// `diag_span_note_once` called for a diagnostic that isn't emitted.
return;
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index bd859d4d6..d4f023958 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -1,8 +1,7 @@
//! Registering limits:
//! * recursion_limit,
-//! * move_size_limit,
-//! * type_length_limit, and
-//! * const_eval_limit
+//! * move_size_limit, and
+//! * type_length_limit
//!
//! There are various parts of the compiler that must impose arbitrary limits
//! on how deeply they recurse to prevent stack overflow. Users can override
@@ -34,12 +33,6 @@ pub fn provide(providers: &mut Providers) {
sym::type_length_limit,
1048576,
),
- const_eval_limit: get_limit(
- tcx.hir().krate_attrs(),
- tcx.sess,
- sym::const_eval_limit,
- 2_000_000,
- ),
}
}
diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs
index f45cf788d..5baeb1ee0 100644
--- a/compiler/rustc_middle/src/middle/privacy.rs
+++ b/compiler/rustc_middle/src/middle/privacy.rs
@@ -4,6 +4,7 @@
use crate::ty::{TyCtxt, Visibility};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_hir::def::DefKind;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -148,13 +149,12 @@ impl EffectiveVisibilities {
};
}
- pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
+ pub fn check_invariants(&self, tcx: TyCtxt<'_>) {
if !cfg!(debug_assertions) {
return;
}
for (&def_id, ev) in &self.map {
// More direct visibility levels can never go farther than less direct ones,
- // neither of effective visibilities can go farther than nominal visibility,
// and all effective visibilities are larger or equal than private visibility.
let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
let span = tcx.def_span(def_id.to_def_id());
@@ -175,17 +175,20 @@ impl EffectiveVisibilities {
ev.reachable_through_impl_trait
);
}
- let nominal_vis = tcx.visibility(def_id);
- // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
- // effective visibilities that are larger than the nominal one.
- if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
- span_bug!(
- span,
- "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
- def_id,
- ev.reachable_through_impl_trait,
- nominal_vis
- );
+ // All effective visibilities except `reachable_through_impl_trait` are limited to
+ // nominal visibility. For some items nominal visibility doesn't make sense so we
+ // don't check this condition for them.
+ if !matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) {
+ let nominal_vis = tcx.visibility(def_id);
+ if !nominal_vis.is_at_least(ev.reachable, tcx) {
+ span_bug!(
+ span,
+ "{:?}: reachable {:?} > nominal {:?}",
+ def_id,
+ ev.reachable,
+ nominal_vis
+ );
+ }
}
}
}
@@ -212,7 +215,7 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
pub fn update(
&mut self,
id: Id,
- nominal_vis: Option<Visibility>,
+ max_vis: Option<Visibility>,
lazy_private_vis: impl FnOnce() -> Visibility,
inherited_effective_vis: EffectiveVisibility,
level: Level,
@@ -236,8 +239,8 @@ impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
&& level != l)
{
- calculated_effective_vis = if let Some(nominal_vis) = nominal_vis && !nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
- nominal_vis
+ calculated_effective_vis = if let Some(max_vis) = max_vis && !max_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
+ max_vis
} else {
inherited_effective_vis_at_level
}
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 6354c0aab..60844c17e 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -104,7 +104,7 @@ pub fn report_unstable(
suggestion: Option<(Span, String, String, Applicability)>,
is_soft: bool,
span: Span,
- soft_handler: impl FnOnce(&'static Lint, Span, &str),
+ soft_handler: impl FnOnce(&'static Lint, Span, String),
) {
let msg = match reason {
Some(r) => format!("use of unstable library feature '{}': {}", feature, r),
@@ -112,7 +112,7 @@ pub fn report_unstable(
};
if is_soft {
- soft_handler(SOFT_UNSTABLE, span, &msg)
+ soft_handler(SOFT_UNSTABLE, span, msg)
} else {
let mut err =
feature_err_issue(&sess.parse_sess, feature, span, GateIssue::Library(issue), msg);
@@ -225,7 +225,7 @@ pub fn deprecation_message_and_lint(
pub fn early_report_deprecation(
lint_buffer: &mut LintBuffer,
- message: &str,
+ message: String,
suggestion: Option<Symbol>,
lint: &'static Lint,
span: Span,
@@ -241,7 +241,7 @@ pub fn early_report_deprecation(
fn late_report_deprecation(
tcx: TyCtxt<'_>,
- message: &str,
+ message: String,
suggestion: Option<Symbol>,
lint: &'static Lint,
span: Span,
@@ -396,7 +396,7 @@ impl<'tcx> TyCtxt<'tcx> {
late_report_deprecation(
self,
- &deprecation_message(
+ deprecation_message(
is_in_effect,
depr_attr.since,
depr_attr.note,
@@ -619,7 +619,7 @@ impl<'tcx> TyCtxt<'tcx> {
allow_unstable: AllowUnstable,
unmarked: impl FnOnce(Span, DefId),
) -> bool {
- let soft_handler = |lint, span, msg: &_| {
+ let soft_handler = |lint, span, msg: String| {
self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, msg, |lint| lint)
};
let eval_result =
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
index 9d70dbfa0..7722e7b47 100644
--- a/compiler/rustc_middle/src/mir/basic_blocks.rs
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -26,7 +26,7 @@ struct Cache {
predecessors: OnceCell<Predecessors>,
switch_sources: OnceCell<SwitchSources>,
is_cyclic: OnceCell<bool>,
- postorder: OnceCell<Vec<BasicBlock>>,
+ reverse_postorder: OnceCell<Vec<BasicBlock>>,
dominators: OnceCell<Dominators<BasicBlock>>,
}
@@ -62,11 +62,14 @@ impl<'tcx> BasicBlocks<'tcx> {
})
}
- /// Returns basic blocks in a postorder.
+ /// Returns basic blocks in a reverse postorder.
#[inline]
- pub fn postorder(&self) -> &[BasicBlock] {
- self.cache.postorder.get_or_init(|| {
- Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect()
+ pub fn reverse_postorder(&self) -> &[BasicBlock] {
+ self.cache.reverse_postorder.get_or_init(|| {
+ let mut rpo: Vec<_> =
+ Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect();
+ rpo.reverse();
+ rpo
})
}
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index 1a8e48264..b8030d9db 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -296,25 +296,13 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
Allocation::from_bytes(slice, Align::ONE, Mutability::Not)
}
- /// Try to create an Allocation of `size` bytes, failing if there is not enough memory
- /// available to the compiler to do so.
- ///
- /// If `panic_on_fail` is true, this will never return `Err`.
- pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> {
- let bytes = Bytes::zeroed(size, align).ok_or_else(|| {
- // This results in an error that can happen non-deterministically, since the memory
- // available to the compiler can change between runs. Normally queries are always
- // deterministic. However, we can be non-deterministic here because all uses of const
- // evaluation (including ConstProp!) will make compilation fail (via hard error
- // or ICE) upon encountering a `MemoryExhausted` error.
- if panic_on_fail {
- panic!("Allocation::uninit called with panic_on_fail had allocation failure")
- }
- ty::tls::with(|tcx| {
- tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
- });
- InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
- })?;
+ fn uninit_inner<R>(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result<Self, R> {
+ // This results in an error that can happen non-deterministically, since the memory
+ // available to the compiler can change between runs. Normally queries are always
+ // deterministic. However, we can be non-deterministic here because all uses of const
+ // evaluation (including ConstProp!) will make compilation fail (via hard error
+ // or ICE) upon encountering a `MemoryExhausted` error.
+ let bytes = Bytes::zeroed(size, align).ok_or_else(fail)?;
Ok(Allocation {
bytes,
@@ -325,6 +313,28 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
extra: (),
})
}
+
+ /// Try to create an Allocation of `size` bytes, failing if there is not enough memory
+ /// available to the compiler to do so.
+ pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> {
+ Self::uninit_inner(size, align, || {
+ ty::tls::with(|tcx| {
+ tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
+ });
+ InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into()
+ })
+ }
+
+ /// Try to create an Allocation of `size` bytes, panics if there is not enough memory
+ /// available to the compiler to do so.
+ pub fn uninit(size: Size, align: Align) -> Self {
+ match Self::uninit_inner(size, align, || {
+ panic!("Allocation::uninit called with panic_on_fail had allocation failure");
+ }) {
+ Ok(x) => x,
+ Err(x) => x,
+ }
+ }
}
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 055d8e9a3..2435bc59e 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -5,11 +5,15 @@ use crate::query::TyCtxtAt;
use crate::ty::{layout, tls, Ty, ValTree};
use rustc_data_structures::sync::Lock;
-use rustc_errors::{pluralize, struct_span_err, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+ struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
+ IntoDiagnosticArg,
+};
use rustc_macros::HashStable;
use rustc_session::CtfeBacktrace;
use rustc_span::def_id::DefId;
-use rustc_target::abi::{call, Align, Size};
+use rustc_target::abi::{call, Align, Size, WrappingRange};
+use std::borrow::Cow;
use std::{any::Any, backtrace::Backtrace, fmt};
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
@@ -91,21 +95,54 @@ pub struct InterpErrorInfo<'tcx>(Box<InterpErrorInfoInner<'tcx>>);
#[derive(Debug)]
struct InterpErrorInfoInner<'tcx> {
kind: InterpError<'tcx>,
+ backtrace: InterpErrorBacktrace,
+}
+
+#[derive(Debug)]
+pub struct InterpErrorBacktrace {
backtrace: Option<Box<Backtrace>>,
}
-impl fmt::Display for InterpErrorInfo<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.0.kind)
+impl InterpErrorBacktrace {
+ pub fn new() -> InterpErrorBacktrace {
+ let capture_backtrace = tls::with_opt(|tcx| {
+ if let Some(tcx) = tcx {
+ *Lock::borrow(&tcx.sess.ctfe_backtrace)
+ } else {
+ CtfeBacktrace::Disabled
+ }
+ });
+
+ let backtrace = match capture_backtrace {
+ CtfeBacktrace::Disabled => None,
+ CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
+ CtfeBacktrace::Immediate => {
+ // Print it now.
+ let backtrace = Backtrace::force_capture();
+ print_backtrace(&backtrace);
+ None
+ }
+ };
+
+ InterpErrorBacktrace { backtrace }
}
-}
-impl<'tcx> InterpErrorInfo<'tcx> {
pub fn print_backtrace(&self) {
- if let Some(backtrace) = self.0.backtrace.as_ref() {
+ if let Some(backtrace) = self.backtrace.as_ref() {
print_backtrace(backtrace);
}
}
+}
+
+impl<'tcx> InterpErrorInfo<'tcx> {
+ pub fn from_parts(kind: InterpError<'tcx>, backtrace: InterpErrorBacktrace) -> Self {
+ Self(Box::new(InterpErrorInfoInner { kind, backtrace }))
+ }
+
+ pub fn into_parts(self) -> (InterpError<'tcx>, InterpErrorBacktrace) {
+ let InterpErrorInfo(box InterpErrorInfoInner { kind, backtrace }) = self;
+ (kind, backtrace)
+ }
pub fn into_kind(self) -> InterpError<'tcx> {
let InterpErrorInfo(box InterpErrorInfoInner { kind, .. }) = self;
@@ -130,32 +167,17 @@ impl From<ErrorGuaranteed> for InterpErrorInfo<'_> {
impl<'tcx> From<InterpError<'tcx>> for InterpErrorInfo<'tcx> {
fn from(kind: InterpError<'tcx>) -> Self {
- let capture_backtrace = tls::with_opt(|tcx| {
- if let Some(tcx) = tcx {
- *Lock::borrow(&tcx.sess.ctfe_backtrace)
- } else {
- CtfeBacktrace::Disabled
- }
- });
-
- let backtrace = match capture_backtrace {
- CtfeBacktrace::Disabled => None,
- CtfeBacktrace::Capture => Some(Box::new(Backtrace::force_capture())),
- CtfeBacktrace::Immediate => {
- // Print it now.
- let backtrace = Backtrace::force_capture();
- print_backtrace(&backtrace);
- None
- }
- };
-
- InterpErrorInfo(Box::new(InterpErrorInfoInner { kind, backtrace }))
+ InterpErrorInfo(Box::new(InterpErrorInfoInner {
+ kind,
+ backtrace: InterpErrorBacktrace::new(),
+ }))
}
}
/// Error information for when the program we executed turned out not to actually be a valid
/// program. This cannot happen in stand-alone Miri, but it can happen during CTFE/ConstProp
/// where we work on generic code or execution does not have all information available.
+#[derive(Debug)]
pub enum InvalidProgramInfo<'tcx> {
/// Resolution can fail if we are in a too generic context.
TooGeneric,
@@ -174,25 +196,6 @@ pub enum InvalidProgramInfo<'tcx> {
UninitUnsizedLocal,
}
-impl fmt::Display for InvalidProgramInfo<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use InvalidProgramInfo::*;
- match self {
- TooGeneric => write!(f, "encountered overly generic constant"),
- AlreadyReported(_) => {
- write!(
- f,
- "an error has already been reported elsewhere (this should not usually be printed)"
- )
- }
- Layout(ref err) => write!(f, "{err}"),
- FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"),
- SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"),
- UninitUnsizedLocal => write!(f, "unsized local is used while uninitialized"),
- }
- }
-}
-
/// Details of why a pointer had to be in-bounds.
#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum CheckInAllocMsg {
@@ -208,26 +211,25 @@ pub enum CheckInAllocMsg {
InboundsTest,
}
-impl fmt::Display for CheckInAllocMsg {
- /// When this is printed as an error the context looks like this:
- /// "{msg}{pointer} is a dangling pointer".
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "{}",
- match *self {
- CheckInAllocMsg::DerefTest => "dereferencing pointer failed: ",
- CheckInAllocMsg::MemoryAccessTest => "memory access failed: ",
- CheckInAllocMsg::PointerArithmeticTest => "out-of-bounds pointer arithmetic: ",
- CheckInAllocMsg::OffsetFromTest => "out-of-bounds offset_from: ",
- CheckInAllocMsg::InboundsTest => "out-of-bounds pointer use: ",
- }
- )
+#[derive(Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
+pub enum InvalidMetaKind {
+ /// Size of a `[T]` is too big
+ SliceTooBig,
+ /// Size of a DST is too big
+ TooBig,
+}
+
+impl IntoDiagnosticArg for InvalidMetaKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Borrowed(match self {
+ InvalidMetaKind::SliceTooBig => "slice_too_big",
+ InvalidMetaKind::TooBig => "too_big",
+ }))
}
}
/// Details of an access to uninitialized bytes where it is not allowed.
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub struct UninitBytesAccess {
/// Range of the original memory access.
pub access: AllocRange,
@@ -242,17 +244,32 @@ pub struct ScalarSizeMismatch {
pub data_size: u64,
}
+macro_rules! impl_into_diagnostic_arg_through_debug {
+ ($($ty:ty),*$(,)?) => {$(
+ impl IntoDiagnosticArg for $ty {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(Cow::Owned(format!("{self:?}")))
+ }
+ }
+ )*}
+}
+
+// These types have nice `Debug` output so we can just use them in diagnostics.
+impl_into_diagnostic_arg_through_debug! {
+ AllocId,
+ Pointer,
+ AllocRange,
+}
+
/// Error information for when the program caused Undefined Behavior.
-pub enum UndefinedBehaviorInfo {
- /// Free-form case. Only for errors that are never caught!
+#[derive(Debug)]
+pub enum UndefinedBehaviorInfo<'a> {
+ /// Free-form case. Only for errors that are never caught! Used by miri
Ub(String),
/// Unreachable code was executed.
Unreachable,
/// A slice/array index projection went out-of-bounds.
- BoundsCheckFailed {
- len: u64,
- index: u64,
- },
+ BoundsCheckFailed { len: u64, index: u64 },
/// Something was divided by 0 (x / 0).
DivisionByZero,
/// Something was "remainded" by 0 (x % 0).
@@ -263,8 +280,8 @@ pub enum UndefinedBehaviorInfo {
RemainderOverflow,
/// Overflowing inbounds pointer arithmetic.
PointerArithOverflow,
- /// Invalid metadata in a wide pointer (using `str` to avoid allocations).
- InvalidMeta(&'static str),
+ /// Invalid metadata in a wide pointer
+ InvalidMeta(InvalidMetaKind),
/// Reading a C string that does not end within its allocation.
UnterminatedCString(Pointer),
/// Dereferencing a dangling pointer after it got freed.
@@ -281,25 +298,13 @@ pub enum UndefinedBehaviorInfo {
/// Using an integer as a pointer in the wrong way.
DanglingIntPointer(u64, CheckInAllocMsg),
/// Used a pointer with bad alignment.
- AlignmentCheckFailed {
- required: Align,
- has: Align,
- },
+ AlignmentCheckFailed { required: Align, has: Align },
/// Writing to read-only memory.
WriteToReadOnly(AllocId),
- // Trying to access the data behind a function pointer.
+ /// Trying to access the data behind a function pointer.
DerefFunctionPointer(AllocId),
- // Trying to access the data behind a vtable pointer.
+ /// Trying to access the data behind a vtable pointer.
DerefVTablePointer(AllocId),
- /// The value validity check found a problem.
- /// Should only be thrown by `validity.rs` and always point out which part of the value
- /// is the problem.
- ValidationFailure {
- /// The "path" to the value in question, e.g. `.0[5].field` for a struct
- /// field in the 6th element of an array that is the first element of a tuple.
- path: Option<String>,
- msg: String,
- },
/// Using a non-boolean `u8` as bool.
InvalidBool(u8),
/// Using a non-character `u32` as character.
@@ -320,110 +325,100 @@ pub enum UndefinedBehaviorInfo {
ScalarSizeMismatch(ScalarSizeMismatch),
/// A discriminant of an uninhabited enum variant is written.
UninhabitedEnumVariantWritten,
+ /// Validation error.
+ Validation(ValidationErrorInfo<'a>),
+ // FIXME(fee1-dead) these should all be actual variants of the enum instead of dynamically
+ // dispatched
+ /// A custom (free-form) error, created by `err_ub_custom!`.
+ Custom(crate::error::CustomSubdiagnostic<'a>),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum PointerKind {
+ Ref,
+ Box,
+}
+
+impl IntoDiagnosticArg for PointerKind {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(
+ match self {
+ Self::Ref => "ref",
+ Self::Box => "box",
+ }
+ .into(),
+ )
+ }
}
-impl fmt::Display for UndefinedBehaviorInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use UndefinedBehaviorInfo::*;
- match self {
- Ub(msg) => write!(f, "{msg}"),
- Unreachable => write!(f, "entering unreachable code"),
- BoundsCheckFailed { ref len, ref index } => {
- write!(f, "indexing out of bounds: the len is {len} but the index is {index}")
- }
- DivisionByZero => write!(f, "dividing by zero"),
- RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"),
- DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"),
- RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"),
- PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"),
- InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"),
- UnterminatedCString(p) => write!(
- f,
- "reading a null-terminated string starting at {p:?} with no null found before end of allocation",
- ),
- PointerUseAfterFree(a) => {
- write!(f, "pointer to {a:?} was dereferenced after this allocation got freed")
- }
- PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size: Size::ZERO, msg } => {
- write!(
- f,
- "{msg}{alloc_id:?} has size {alloc_size}, so pointer at offset {ptr_offset} is out-of-bounds",
- alloc_size = alloc_size.bytes(),
- )
- }
- PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => write!(
- f,
- "{msg}{alloc_id:?} has size {alloc_size}, so pointer to {ptr_size} byte{ptr_size_p} starting at offset {ptr_offset} is out-of-bounds",
- alloc_size = alloc_size.bytes(),
- ptr_size = ptr_size.bytes(),
- ptr_size_p = pluralize!(ptr_size.bytes()),
- ),
- DanglingIntPointer(i, msg) => {
- write!(
- f,
- "{msg}{pointer} is a dangling pointer (it has no provenance)",
- pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
- )
- }
- AlignmentCheckFailed { required, has } => write!(
- f,
- "accessing memory with alignment {has}, but alignment {required} is required",
- has = has.bytes(),
- required = required.bytes()
- ),
- WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"),
- DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"),
- DerefVTablePointer(a) => write!(f, "accessing {a:?} which contains a vtable"),
- ValidationFailure { path: None, msg } => {
- write!(f, "constructing invalid value: {msg}")
- }
- ValidationFailure { path: Some(path), msg } => {
- write!(f, "constructing invalid value at {path}: {msg}")
- }
- InvalidBool(b) => {
- write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}")
- }
- InvalidChar(c) => {
- write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}")
- }
- InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"),
- InvalidFunctionPointer(p) => {
- write!(f, "using {p:?} as function pointer but it does not point to a function")
- }
- InvalidVTablePointer(p) => {
- write!(f, "using {p:?} as vtable pointer but it does not point to a vtable")
- }
- InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"),
- InvalidUninitBytes(Some((alloc, info))) => write!(
- f,
- "reading memory at {alloc:?}{access:?}, \
- but memory is uninitialized at {uninit:?}, \
- and this operation requires initialized memory",
- access = info.access,
- uninit = info.uninit,
- ),
- InvalidUninitBytes(None) => write!(
- f,
- "using uninitialized data, but this operation requires initialized memory"
- ),
- DeadLocal => write!(f, "accessing a dead local variable"),
- ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!(
- f,
- "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead",
- ),
- UninhabitedEnumVariantWritten => {
- write!(f, "writing discriminant of an uninhabited enum")
- }
+#[derive(Debug)]
+pub struct ValidationErrorInfo<'tcx> {
+ pub path: Option<String>,
+ pub kind: ValidationErrorKind<'tcx>,
+}
+
+#[derive(Debug)]
+pub enum ExpectedKind {
+ Reference,
+ Box,
+ RawPtr,
+ InitScalar,
+ Bool,
+ Char,
+ Float,
+ Int,
+ FnPtr,
+}
+
+impl From<PointerKind> for ExpectedKind {
+ fn from(x: PointerKind) -> ExpectedKind {
+ match x {
+ PointerKind::Box => ExpectedKind::Box,
+ PointerKind::Ref => ExpectedKind::Reference,
}
}
}
+#[derive(Debug)]
+pub enum ValidationErrorKind<'tcx> {
+ PtrToUninhabited { ptr_kind: PointerKind, ty: Ty<'tcx> },
+ PtrToStatic { ptr_kind: PointerKind },
+ PtrToMut { ptr_kind: PointerKind },
+ ExpectedNonPtr { value: String },
+ MutableRefInConst,
+ NullFnPtr,
+ NeverVal,
+ NullablePtrOutOfRange { range: WrappingRange, max_value: u128 },
+ PtrOutOfRange { range: WrappingRange, max_value: u128 },
+ OutOfRange { value: String, range: WrappingRange, max_value: u128 },
+ UnsafeCell,
+ UninhabitedVal { ty: Ty<'tcx> },
+ InvalidEnumTag { value: String },
+ UninitEnumTag,
+ UninitStr,
+ Uninit { expected: ExpectedKind },
+ UninitVal,
+ InvalidVTablePtr { value: String },
+ InvalidMetaSliceTooLarge { ptr_kind: PointerKind },
+ InvalidMetaTooLarge { ptr_kind: PointerKind },
+ UnalignedPtr { ptr_kind: PointerKind, required_bytes: u64, found_bytes: u64 },
+ NullPtr { ptr_kind: PointerKind },
+ DanglingPtrNoProvenance { ptr_kind: PointerKind, pointer: String },
+ DanglingPtrOutOfBounds { ptr_kind: PointerKind },
+ DanglingPtrUseAfterFree { ptr_kind: PointerKind },
+ InvalidBool { value: String },
+ InvalidChar { value: String },
+ InvalidFnPtr { value: String },
+}
+
/// Error information for when the program did something that might (or might not) be correct
/// to do according to the Rust spec, but due to limitations in the interpreter, the
/// operation could not be carried out. These limitations can differ between CTFE and the
/// Miri engine, e.g., CTFE does not support dereferencing pointers at integral addresses.
+#[derive(Debug)]
pub enum UnsupportedOpInfo {
/// Free-form case. Only for errors that are never caught!
+ // FIXME still use translatable diagnostics
Unsupported(String),
//
// The variants below are only reachable from CTFE/const prop, miri will never emit them.
@@ -442,83 +437,42 @@ pub enum UnsupportedOpInfo {
ReadExternStatic(DefId),
}
-impl fmt::Display for UnsupportedOpInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use UnsupportedOpInfo::*;
- match self {
- Unsupported(ref msg) => write!(f, "{msg}"),
- PartialPointerOverwrite(ptr) => {
- write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}")
- }
- PartialPointerCopy(ptr) => {
- write!(f, "unable to copy parts of a pointer from memory at {ptr:?}")
- }
- ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"),
- ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"),
- ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"),
- }
- }
-}
-
/// Error information for when the program exhausted the resources granted to it
/// by the interpreter.
+#[derive(Debug)]
pub enum ResourceExhaustionInfo {
/// The stack grew too big.
StackFrameLimitReached,
- /// The program ran for too long.
- ///
- /// The exact limit is set by the `const_eval_limit` attribute.
- StepLimitReached,
/// There is not enough memory (on the host) to perform an allocation.
MemoryExhausted,
/// The address space (of the target) is full.
AddressSpaceFull,
}
-impl fmt::Display for ResourceExhaustionInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use ResourceExhaustionInfo::*;
- match self {
- StackFrameLimitReached => {
- write!(f, "reached the configured maximum number of stack frames")
- }
- StepLimitReached => {
- write!(f, "exceeded interpreter step limit (see `#[const_eval_limit]`)")
- }
- MemoryExhausted => {
- write!(f, "tried to allocate more memory than available to compiler")
- }
- AddressSpaceFull => {
- write!(f, "there are no more free addresses in the address space")
- }
- }
- }
-}
-
-/// A trait to work around not having trait object upcasting.
-pub trait AsAny: Any {
- fn as_any(&self) -> &dyn Any;
-}
-impl<T: Any> AsAny for T {
- #[inline(always)]
- fn as_any(&self) -> &dyn Any {
- self
- }
-}
-
/// A trait for machine-specific errors (or other "machine stop" conditions).
-pub trait MachineStopType: AsAny + fmt::Display + Send {}
+pub trait MachineStopType: Any + fmt::Debug + Send {
+ /// The diagnostic message for this error
+ fn diagnostic_message(&self) -> DiagnosticMessage;
+ /// Add diagnostic arguments by passing name and value pairs to `adder`, which are passed to
+ /// fluent for formatting the translated diagnostic message.
+ fn add_args(
+ self: Box<Self>,
+ adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>),
+ );
+}
impl dyn MachineStopType {
#[inline(always)]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
- self.as_any().downcast_ref()
+ let x: &dyn Any = self;
+ x.downcast_ref()
}
}
+#[derive(Debug)]
pub enum InterpError<'tcx> {
/// The program caused undefined behavior.
- UndefinedBehavior(UndefinedBehaviorInfo),
+ UndefinedBehavior(UndefinedBehaviorInfo<'tcx>),
/// The program did something the interpreter does not support (some of these *might* be UB
/// but the interpreter is not sure).
Unsupported(UnsupportedOpInfo),
@@ -534,26 +488,6 @@ pub enum InterpError<'tcx> {
pub type InterpResult<'tcx, T = ()> = Result<T, InterpErrorInfo<'tcx>>;
-impl fmt::Display for InterpError<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- use InterpError::*;
- match *self {
- Unsupported(ref msg) => write!(f, "{msg}"),
- InvalidProgram(ref msg) => write!(f, "{msg}"),
- UndefinedBehavior(ref msg) => write!(f, "{msg}"),
- ResourceExhaustion(ref msg) => write!(f, "{msg}"),
- MachineStop(ref msg) => write!(f, "{msg}"),
- }
- }
-}
-
-// Forward `Debug` to `Display`, so it does not look awful.
-impl fmt::Debug for InterpError<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(self, f)
- }
-}
-
impl InterpError<'_> {
/// Some errors do string formatting even if the error is never printed.
/// To avoid performance issues, there are places where we want to be sure to never raise these formatting errors,
@@ -562,7 +496,7 @@ impl InterpError<'_> {
matches!(
self,
InterpError::Unsupported(UnsupportedOpInfo::Unsupported(_))
- | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::ValidationFailure { .. })
+ | InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Validation { .. })
| InterpError::UndefinedBehavior(UndefinedBehaviorInfo::Ub(_))
)
}
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 3620385fa..2d2cfee1b 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -89,6 +89,30 @@ macro_rules! throw_machine_stop {
($($tt:tt)*) => { do yeet err_machine_stop!($($tt)*) };
}
+#[macro_export]
+macro_rules! err_ub_custom {
+ ($msg:expr $(, $($name:ident = $value:expr),* $(,)?)?) => {{
+ $(
+ let ($($name,)*) = ($($value,)*);
+ )?
+ err_ub!(Custom(
+ rustc_middle::error::CustomSubdiagnostic {
+ msg: || $msg,
+ add_args: Box::new(move |mut set_arg| {
+ $($(
+ set_arg(stringify!($name).into(), rustc_errors::IntoDiagnosticArg::into_diagnostic_arg($name));
+ )*)?
+ })
+ }
+ ))
+ }};
+}
+
+#[macro_export]
+macro_rules! throw_ub_custom {
+ ($($tt:tt)*) => { do yeet err_ub_custom!($($tt)*) };
+}
+
mod allocation;
mod error;
mod pointer;
@@ -119,9 +143,10 @@ use crate::ty::{self, Instance, Ty, TyCtxt};
pub use self::error::{
struct_error, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult, EvalToConstValueResult,
- EvalToValTreeResult, InterpError, InterpErrorInfo, InterpResult, InvalidProgramInfo,
- MachineStopType, ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch,
- UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
+ EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind,
+ InvalidProgramInfo, MachineStopType, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
+ ScalarSizeMismatch, UndefinedBehaviorInfo, UninitBytesAccess, UnsupportedOpInfo,
+ ValidationErrorInfo, ValidationErrorKind,
};
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index f53dc8cb0..9c97431f3 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -95,8 +95,15 @@ impl<'tcx> TyCtxt<'tcx> {
// used generic parameters is a bug of evaluation, so checking for it
// here does feel somewhat sensible.
if !self.features().generic_const_exprs && ct.substs.has_non_region_param() {
- assert!(matches!(self.def_kind(ct.def), DefKind::AnonConst));
- let mir_body = self.mir_for_ctfe(ct.def);
+ let def_kind = self.def_kind(instance.def_id());
+ assert!(
+ matches!(
+ def_kind,
+ DefKind::InlineConst | DefKind::AnonConst | DefKind::AssocConst
+ ),
+ "{cid:?} is {def_kind:?}",
+ );
+ let mir_body = self.mir_for_ctfe(instance.def_id());
if mir_body.is_polymorphic {
let Some(local_def_id) = ct.def.as_local() else { return };
self.struct_span_lint_hir(
@@ -236,15 +243,3 @@ impl<'tcx> TyCtxtEnsure<'tcx> {
self.eval_to_allocation_raw(param_env.and(gid))
}
}
-
-impl<'tcx> TyCtxt<'tcx> {
- /// Destructure a mir constant ADT or array into its variant index and its field values.
- /// Panics if the destructuring fails, use `try_destructure_mir_constant` for fallible version.
- pub fn destructure_mir_constant(
- self,
- param_env: ty::ParamEnv<'tcx>,
- constant: mir::ConstantKind<'tcx>,
- ) -> mir::DestructuredConstant<'tcx> {
- self.try_destructure_mir_constant(param_env.and(constant)).unwrap()
- }
-}
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 36dbbe4bf..0416411df 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -97,6 +97,10 @@ impl<'tcx> ConstValue<'tcx> {
ConstValue::Scalar(Scalar::from_u64(i))
}
+ pub fn from_u128(i: u128) -> Self {
+ ConstValue::Scalar(Scalar::from_u128(i))
+ }
+
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
ConstValue::Scalar(Scalar::from_target_usize(i, cx))
}
@@ -241,6 +245,11 @@ impl<Prov> Scalar<Prov> {
}
#[inline]
+ pub fn from_u128(i: u128) -> Self {
+ Scalar::Int(i.into())
+ }
+
+ #[inline]
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
Self::from_uint(i, cx.data_layout().pointer_size)
}
@@ -375,7 +384,8 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_bits(self, target_size: Size) -> u128 {
- self.to_bits(target_size).unwrap()
+ self.to_bits(target_size)
+ .unwrap_or_else(|_| panic!("assertion failed: {self:?} fits {target_size:?}"))
}
pub fn to_bool(self) -> InterpResult<'tcx, bool> {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 5c71910a9..28c505878 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -3,7 +3,7 @@
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
use crate::mir::interpret::{
- AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, LitToConstInput, Scalar,
+ AllocRange, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
};
use crate::mir::visit::MirVisitable;
use crate::ty::codec::{TyDecoder, TyEncoder};
@@ -15,7 +15,7 @@ use crate::ty::{AdtDef, InstanceDef, ScalarInt, UserTypeAnnotationIndex};
use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
use rustc_data_structures::captures::Captures;
-use rustc_errors::ErrorGuaranteed;
+use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::{self, GeneratorKind, ImplicitSelfKind};
@@ -145,7 +145,7 @@ impl MirPhase {
}
"analysis" => Self::Analysis(AnalysisPhase::parse(phase)),
"runtime" => Self::Runtime(RuntimePhase::parse(phase)),
- _ => panic!("Unknown MIR dialect {}", dialect),
+ _ => bug!("Unknown MIR dialect: '{}'", dialect),
}
}
}
@@ -159,7 +159,7 @@ impl AnalysisPhase {
match &*phase.to_ascii_lowercase() {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
- _ => panic!("Unknown analysis phase {}", phase),
+ _ => bug!("Unknown analysis phase: '{}'", phase),
}
}
}
@@ -174,7 +174,7 @@ impl RuntimePhase {
"initial" => Self::Initial,
"post_cleanup" | "post-cleanup" | "postcleanup" => Self::PostCleanup,
"optimized" => Self::Optimized,
- _ => panic!("Unknown runtime phase {}", phase),
+ _ => bug!("Unknown runtime phase: '{}'", phase),
}
}
}
@@ -476,7 +476,7 @@ impl<'tcx> Body<'tcx> {
/// Returns the return type; it always return first element from `local_decls` array.
#[inline]
pub fn bound_return_ty(&self) -> ty::EarlyBinder<Ty<'tcx>> {
- ty::EarlyBinder(self.local_decls[RETURN_PLACE].ty)
+ ty::EarlyBinder::bind(self.local_decls[RETURN_PLACE].ty)
}
/// Gets the location of the terminator for the given block.
@@ -1371,55 +1371,61 @@ impl<O> AssertKind<O> {
_ => write!(f, "\"{}\"", self.description()),
}
}
-}
-impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ pub fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
use AssertKind::*;
+
match self {
- BoundsCheck { ref len, ref index } => write!(
- f,
- "index out of bounds: the length is {:?} but the index is {:?}",
- len, index
- ),
- OverflowNeg(op) => write!(f, "attempt to negate `{:#?}`, which would overflow", op),
- DivisionByZero(op) => write!(f, "attempt to divide `{:#?}` by zero", op),
- RemainderByZero(op) => write!(
- f,
- "attempt to calculate the remainder of `{:#?}` with a divisor of zero",
- op
- ),
- Overflow(BinOp::Add, l, r) => {
- write!(f, "attempt to compute `{:#?} + {:#?}`, which would overflow", l, r)
- }
- Overflow(BinOp::Sub, l, r) => {
- write!(f, "attempt to compute `{:#?} - {:#?}`, which would overflow", l, r)
- }
- Overflow(BinOp::Mul, l, r) => {
- write!(f, "attempt to compute `{:#?} * {:#?}`, which would overflow", l, r)
- }
- Overflow(BinOp::Div, l, r) => {
- write!(f, "attempt to compute `{:#?} / {:#?}`, which would overflow", l, r)
+ BoundsCheck { .. } => middle_bounds_check,
+ Overflow(BinOp::Shl, _, _) => middle_assert_shl_overflow,
+ Overflow(BinOp::Shr, _, _) => middle_assert_shr_overflow,
+ Overflow(_, _, _) => middle_assert_op_overflow,
+ OverflowNeg(_) => middle_assert_overflow_neg,
+ DivisionByZero(_) => middle_assert_divide_by_zero,
+ RemainderByZero(_) => middle_assert_remainder_by_zero,
+ ResumedAfterReturn(GeneratorKind::Async(_)) => middle_assert_async_resume_after_return,
+ ResumedAfterReturn(GeneratorKind::Gen) => middle_assert_generator_resume_after_return,
+ ResumedAfterPanic(GeneratorKind::Async(_)) => middle_assert_async_resume_after_panic,
+ ResumedAfterPanic(GeneratorKind::Gen) => middle_assert_generator_resume_after_panic,
+
+ MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref,
+ }
+ }
+
+ pub fn add_args(self, adder: &mut dyn FnMut(Cow<'static, str>, DiagnosticArgValue<'static>))
+ where
+ O: fmt::Debug,
+ {
+ use AssertKind::*;
+
+ macro_rules! add {
+ ($name: expr, $value: expr) => {
+ adder($name.into(), $value.into_diagnostic_arg());
+ };
+ }
+
+ match self {
+ BoundsCheck { len, index } => {
+ add!("len", format!("{len:?}"));
+ add!("index", format!("{index:?}"));
}
- Overflow(BinOp::Rem, l, r) => write!(
- f,
- "attempt to compute the remainder of `{:#?} % {:#?}`, which would overflow",
- l, r
- ),
- Overflow(BinOp::Shr, _, r) => {
- write!(f, "attempt to shift right by `{:#?}`, which would overflow", r)
+ Overflow(BinOp::Shl | BinOp::Shr, _, val)
+ | DivisionByZero(val)
+ | RemainderByZero(val)
+ | OverflowNeg(val) => {
+ add!("val", format!("{val:#?}"));
}
- Overflow(BinOp::Shl, _, r) => {
- write!(f, "attempt to shift left by `{:#?}`, which would overflow", r)
+ Overflow(binop, left, right) => {
+ add!("op", binop.to_hir_binop().as_str());
+ add!("left", format!("{left:#?}"));
+ add!("right", format!("{right:#?}"));
}
+ ResumedAfterReturn(_) | ResumedAfterPanic(_) => {}
MisalignedPointerDereference { required, found } => {
- write!(
- f,
- "misaligned pointer dereference: address must be a multiple of {:?} but is {:?}",
- required, found
- )
+ add!("required", format!("{required:#?}"));
+ add!("found", format!("{found:#?}"));
}
- _ => write!(f, "{}", self.description()),
}
}
}
@@ -1907,7 +1913,7 @@ impl<'tcx> Operand<'tcx> {
substs: impl IntoIterator<Item = GenericArg<'tcx>>,
span: Span,
) -> Self {
- let ty = tcx.mk_fn_def(def_id, substs);
+ let ty = Ty::new_fn_def(tcx, def_id, substs);
Operand::Constant(Box::new(Constant {
span,
user_ty: None,
@@ -2008,7 +2014,7 @@ impl<'tcx> Rvalue<'tcx> {
| CastKind::IntToFloat
| CastKind::FnPtrToPtr
| CastKind::PtrToPtr
- | CastKind::Pointer(_)
+ | CastKind::PointerCoercion(_)
| CastKind::PointerFromExposedAddress
| CastKind::DynStar
| CastKind::Transmute,
@@ -2029,23 +2035,19 @@ impl<'tcx> Rvalue<'tcx> {
impl BorrowKind {
pub fn mutability(&self) -> Mutability {
match *self {
- BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => Mutability::Not,
+ BorrowKind::Shared | BorrowKind::Shallow => Mutability::Not,
BorrowKind::Mut { .. } => Mutability::Mut,
}
}
pub fn allows_two_phase_borrow(&self) -> bool {
match *self {
- BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => false,
- BorrowKind::Mut { allow_two_phase_borrow } => allow_two_phase_borrow,
- }
- }
-
- // FIXME: won't be used after diagnostic migration
- pub fn describe_mutability(&self) -> &str {
- match *self {
- BorrowKind::Shared | BorrowKind::Shallow | BorrowKind::Unique => "immutable",
- BorrowKind::Mut { .. } => "mutable",
+ BorrowKind::Shared
+ | BorrowKind::Shallow
+ | BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::ClosureCapture } => {
+ false
+ }
+ BorrowKind::Mut { kind: MutBorrowKind::TwoPhaseBorrow } => true,
}
}
}
@@ -2084,7 +2086,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
let kind_str = match borrow_kind {
BorrowKind::Shared => "",
BorrowKind::Shallow => "shallow ",
- BorrowKind::Mut { .. } | BorrowKind::Unique => "mut ",
+ BorrowKind::Mut { .. } => "mut ",
};
// When printing regions, add trailing space if necessary.
@@ -2327,10 +2329,10 @@ impl<'tcx> ConstantKind<'tcx> {
pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
match self {
Self::Ty(c) => {
- if let Some(val) = c.kind().try_eval_for_mir(tcx, param_env) {
+ if let Some(val) = c.try_eval_for_mir(tcx, param_env) {
match val {
Ok(val) => Self::Val(val, c.ty()),
- Err(guar) => Self::Ty(tcx.const_error(self.ty(), guar)),
+ Err(guar) => Self::Ty(ty::Const::new_error(tcx, guar, self.ty())),
}
} else {
self
@@ -2342,7 +2344,9 @@ impl<'tcx> ConstantKind<'tcx> {
match tcx.const_eval_resolve(param_env, uneval, None) {
Ok(val) => Self::Val(val, ty),
Err(ErrorHandled::TooGeneric) => self,
- Err(ErrorHandled::Reported(guar)) => Self::Ty(tcx.const_error(ty, guar.into())),
+ Err(ErrorHandled::Reported(guar)) => {
+ Self::Ty(ty::Const::new_error(tcx, guar.into(), ty))
+ }
}
}
}
@@ -2461,51 +2465,6 @@ impl<'tcx> ConstantKind<'tcx> {
Self::Val(val, ty)
}
- #[instrument(skip(tcx), level = "debug", ret)]
- pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let body_id = match tcx.hir().get(hir_id) {
- hir::Node::AnonConst(ac) => ac.body,
- _ => span_bug!(
- tcx.def_span(def_id.to_def_id()),
- "from_inline_const can only process anonymous constants"
- ),
- };
- let expr = &tcx.hir().body(body_id).value;
- let ty = tcx.typeck(def_id).node_type(hir_id);
-
- let lit_input = match expr.kind {
- hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
- hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
- hir::ExprKind::Lit(ref lit) => {
- Some(LitToConstInput { lit: &lit.node, ty, neg: true })
- }
- _ => None,
- },
- _ => None,
- };
- if let Some(lit_input) = lit_input {
- // If an error occurred, ignore that it's a literal and leave reporting the error up to
- // mir.
- match tcx.at(expr.span).lit_to_mir_constant(lit_input) {
- Ok(c) => return c,
- Err(_) => {}
- }
- }
-
- let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
- let parent_substs =
- tcx.erase_regions(InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
- let substs =
- ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
- .substs;
-
- let uneval = UnevaluatedConst { def: def_id.to_def_id(), substs, promoted: None };
- debug_assert!(!uneval.has_free_regions());
-
- Self::Unevaluated(uneval, ty)
- }
-
/// Literals are converted to `ConstantKindVal`, const generic parameters are eagerly
/// converted to a constant, everything else becomes `Unevaluated`.
#[instrument(skip(tcx), level = "debug", ret)]
@@ -2553,7 +2512,7 @@ impl<'tcx> ConstantKind<'tcx> {
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.item_name(def_id);
- let ty_const = tcx.mk_const(ty::ParamConst::new(index, name), ty);
+ let ty_const = ty::Const::new_param(tcx, ty::ParamConst::new(index, name), ty);
debug!(?ty_const);
return Self::Ty(ty_const);
@@ -2624,10 +2583,9 @@ pub struct UnevaluatedConst<'tcx> {
}
impl<'tcx> UnevaluatedConst<'tcx> {
- // FIXME: probably should get rid of this method. It's also wrong to
- // shrink and then later expand a promoted.
#[inline]
pub fn shrink(self) -> ty::UnevaluatedConst<'tcx> {
+ assert_eq!(self.promoted, None);
ty::UnevaluatedConst { def: self.def, substs: self.substs }
}
}
@@ -2818,7 +2776,7 @@ impl<'tcx> Display for ConstantKind<'tcx> {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
ConstantKind::Ty(c) => pretty_print_const(c, fmt, true),
- ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt, true),
+ ConstantKind::Val(val, ty) => pretty_print_const_value(val, ty, fmt),
// FIXME(valtrees): Correctly print mir constants.
ConstantKind::Unevaluated(..) => {
fmt.write_str("_")?;
@@ -2848,13 +2806,16 @@ fn pretty_print_byte_str(fmt: &mut Formatter<'_>, byte_str: &[u8]) -> fmt::Resul
write!(fmt, "b\"{}\"", byte_str.escape_ascii())
}
-fn comma_sep<'tcx>(fmt: &mut Formatter<'_>, elems: Vec<ConstantKind<'tcx>>) -> fmt::Result {
+fn comma_sep<'tcx>(
+ fmt: &mut Formatter<'_>,
+ elems: Vec<(ConstValue<'tcx>, Ty<'tcx>)>,
+) -> fmt::Result {
let mut first = true;
- for elem in elems {
+ for (ct, ty) in elems {
if !first {
fmt.write_str(", ")?;
}
- fmt.write_str(&format!("{}", elem))?;
+ pretty_print_const_value(ct, ty, fmt)?;
first = false;
}
Ok(())
@@ -2865,7 +2826,6 @@ fn pretty_print_const_value<'tcx>(
ct: ConstValue<'tcx>,
ty: Ty<'tcx>,
fmt: &mut Formatter<'_>,
- print_ty: bool,
) -> fmt::Result {
use crate::ty::print::PrettyPrinter;
@@ -2909,7 +2869,7 @@ fn pretty_print_const_value<'tcx>(
}
}
(ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
- let n = n.kind().try_to_bits(tcx.data_layout.pointer_size).unwrap();
+ let n = n.try_to_bits(tcx.data_layout.pointer_size).unwrap();
// cast is ok because we already checked for pointer size (32 or 64 bit) above
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();
@@ -2924,16 +2884,11 @@ fn pretty_print_const_value<'tcx>(
// introducing ICEs (e.g. via `layout_of`) from missing bounds.
// E.g. `transmute([0usize; 2]): (u8, *mut T)` needs to know `T: Sized`
// to be able to destructure the tuple into `(0u8, *mut T)`
- //
- // FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the
- // correct `ty::ParamEnv` to allow printing *all* constant values.
(_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_non_region_param() => {
let ct = tcx.lift(ct).unwrap();
let ty = tcx.lift(ty).unwrap();
- if let Some(contents) = tcx.try_destructure_mir_constant(
- ty::ParamEnv::reveal_all().and(ConstantKind::Val(ct, ty)),
- ) {
- let fields = contents.fields.to_vec();
+ if let Some(contents) = tcx.try_destructure_mir_constant_for_diagnostics((ct, ty)) {
+ let fields: Vec<(ConstValue<'_>, Ty<'_>)> = contents.fields.to_vec();
match *ty.kind() {
ty::Array(..) => {
fmt.write_str("[")?;
@@ -2972,12 +2927,14 @@ fn pretty_print_const_value<'tcx>(
None => {
fmt.write_str(" {{ ")?;
let mut first = true;
- for (field_def, field) in iter::zip(&variant_def.fields, fields)
+ for (field_def, (ct, ty)) in
+ iter::zip(&variant_def.fields, fields)
{
if !first {
fmt.write_str(", ")?;
}
- fmt.write_str(&format!("{}: {}", field_def.name, field))?;
+ write!(fmt, "{}: ", field_def.name)?;
+ pretty_print_const_value(ct, ty, fmt)?;
first = false;
}
fmt.write_str(" }}")?;
@@ -2987,20 +2944,13 @@ fn pretty_print_const_value<'tcx>(
_ => unreachable!(),
}
return Ok(());
- } else {
- // Fall back to debug pretty printing for invalid constants.
- fmt.write_str(&format!("{:?}", ct))?;
- if print_ty {
- fmt.write_str(&format!(": {}", ty))?;
- }
- return Ok(());
- };
+ }
}
(ConstValue::Scalar(scalar), _) => {
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
cx.print_alloc_ids = true;
let ty = tcx.lift(ty).unwrap();
- cx = cx.pretty_print_const_scalar(scalar, ty, print_ty)?;
+ cx = cx.pretty_print_const_scalar(scalar, ty)?;
fmt.write_str(&cx.into_buffer())?;
return Ok(());
}
@@ -3015,12 +2965,8 @@ fn pretty_print_const_value<'tcx>(
// their fields instead of just dumping the memory.
_ => {}
}
- // fallback
- fmt.write_str(&format!("{:?}", ct))?;
- if print_ty {
- fmt.write_str(&format!(": {}", ty))?;
- }
- Ok(())
+ // Fall back to debug pretty printing for invalid constants.
+ write!(fmt, "{ct:?}: {ty}")
})
}
diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs
index f31b343c9..ca735d523 100644
--- a/compiler/rustc_middle/src/mir/mono.rs
+++ b/compiler/rustc_middle/src/mir/mono.rs
@@ -231,7 +231,7 @@ pub struct CodegenUnit<'tcx> {
/// as well as the crate name and disambiguator.
name: Symbol,
items: FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)>,
- size_estimate: Option<usize>,
+ size_estimate: usize,
primary: bool,
/// True if this is CGU is used to hold code coverage information for dead code,
/// false otherwise.
@@ -269,7 +269,7 @@ impl<'tcx> CodegenUnit<'tcx> {
CodegenUnit {
name,
items: Default::default(),
- size_estimate: None,
+ size_estimate: 0,
primary: false,
is_code_coverage_dead_code_cgu: false,
}
@@ -291,10 +291,12 @@ impl<'tcx> CodegenUnit<'tcx> {
self.primary = true;
}
+ /// The order of these items is non-determinstic.
pub fn items(&self) -> &FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)> {
&self.items
}
+ /// The order of these items is non-determinstic.
pub fn items_mut(&mut self) -> &mut FxHashMap<MonoItem<'tcx>, (Linkage, Visibility)> {
&mut self.items
}
@@ -318,23 +320,21 @@ impl<'tcx> CodegenUnit<'tcx> {
base_n::encode(hash, base_n::CASE_INSENSITIVE)
}
- pub fn create_size_estimate(&mut self, tcx: TyCtxt<'tcx>) {
+ pub fn compute_size_estimate(&mut self, tcx: TyCtxt<'tcx>) {
// Estimate the size of a codegen unit as (approximately) the number of MIR
// statements it corresponds to.
- self.size_estimate = Some(self.items.keys().map(|mi| mi.size_estimate(tcx)).sum());
+ self.size_estimate = self.items.keys().map(|mi| mi.size_estimate(tcx)).sum();
}
#[inline]
- /// Should only be called if [`create_size_estimate`] has previously been called.
+ /// Should only be called if [`compute_size_estimate`] has previously been called.
///
- /// [`create_size_estimate`]: Self::create_size_estimate
+ /// [`compute_size_estimate`]: Self::compute_size_estimate
pub fn size_estimate(&self) -> usize {
+ // Items are never zero-sized, so if we have items the estimate must be
+ // non-zero, unless we forgot to call `compute_size_estimate` first.
+ assert!(self.items.is_empty() || self.size_estimate != 0);
self.size_estimate
- .expect("create_size_estimate must be called before getting a size_estimate")
- }
-
- pub fn modify_size_estimate(&mut self, delta: usize) {
- *self.size_estimate.as_mut().unwrap() += delta;
}
pub fn contains_item(&self, item: &MonoItem<'tcx>) -> bool {
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 62c3d8cf2..ffa7a5400 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -353,14 +353,22 @@ where
for statement in &data.statements {
extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_body = format!("{0}{0}{1:?};", INDENT, statement);
- writeln!(
- w,
- "{:A$} // {}{}",
- indented_body,
- if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
- comment(tcx, statement.source_info, body.span),
- A = ALIGN,
- )?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{:A$} // {}{}",
+ indented_body,
+ if tcx.sess.verbose() {
+ format!("{:?}: ", current_location)
+ } else {
+ String::new()
+ },
+ comment(tcx, statement.source_info),
+ A = ALIGN,
+ )?;
+ } else {
+ writeln!(w, "{}", indented_body)?;
+ }
write_extra(tcx, w, |visitor| {
visitor.visit_statement(statement, current_location);
@@ -374,14 +382,18 @@ where
// Terminator at the bottom.
extra_data(PassWhere::BeforeLocation(current_location), w)?;
let indented_terminator = format!("{0}{0}{1:?};", INDENT, data.terminator().kind);
- writeln!(
- w,
- "{:A$} // {}{}",
- indented_terminator,
- if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
- comment(tcx, data.terminator().source_info, body.span),
- A = ALIGN,
- )?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{:A$} // {}{}",
+ indented_terminator,
+ if tcx.sess.verbose() { format!("{:?}: ", current_location) } else { String::new() },
+ comment(tcx, data.terminator().source_info),
+ A = ALIGN,
+ )?;
+ } else {
+ writeln!(w, "{}", indented_terminator)?;
+ }
write_extra(tcx, w, |visitor| {
visitor.visit_terminator(data.terminator(), current_location);
@@ -400,10 +412,12 @@ fn write_extra<'tcx, F>(tcx: TyCtxt<'tcx>, write: &mut dyn Write, mut visit_op:
where
F: FnMut(&mut ExtraComments<'tcx>),
{
- let mut extra_comments = ExtraComments { tcx, comments: vec![] };
- visit_op(&mut extra_comments);
- for comment in extra_comments.comments {
- writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ let mut extra_comments = ExtraComments { tcx, comments: vec![] };
+ visit_op(&mut extra_comments);
+ for comment in extra_comments.comments {
+ writeln!(write, "{:A$} // {}", "", comment, A = ALIGN)?;
+ }
}
Ok(())
}
@@ -522,13 +536,8 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
}
}
-fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo, function_span: Span) -> String {
- let location = if tcx.sess.opts.unstable_opts.mir_pretty_relative_line_numbers {
- tcx.sess.source_map().span_to_relative_line_string(span, function_span)
- } else {
- tcx.sess.source_map().span_to_embeddable_string(span)
- };
-
+fn comment(tcx: TyCtxt<'_>, SourceInfo { span, scope }: SourceInfo) -> String {
+ let location = tcx.sess.source_map().span_to_embeddable_string(span);
format!("scope {} at {}", scope.index(), location,)
}
@@ -560,13 +569,17 @@ fn write_scope_tree(
var_debug_info.value,
);
- writeln!(
- w,
- "{0:1$} // in {2}",
- indented_debug_info,
- ALIGN,
- comment(tcx, var_debug_info.source_info, body.span),
- )?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{0:1$} // in {2}",
+ indented_debug_info,
+ ALIGN,
+ comment(tcx, var_debug_info.source_info),
+ )?;
+ } else {
+ writeln!(w, "{}", indented_debug_info)?;
+ }
}
// Local variable types.
@@ -594,14 +607,18 @@ fn write_scope_tree(
let local_name = if local == RETURN_PLACE { " return place" } else { "" };
- writeln!(
- w,
- "{0:1$} //{2} in {3}",
- indented_decl,
- ALIGN,
- local_name,
- comment(tcx, local_decl.source_info, body.span),
- )?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ writeln!(
+ w,
+ "{0:1$} //{2} in {3}",
+ indented_decl,
+ ALIGN,
+ local_name,
+ comment(tcx, local_decl.source_info),
+ )?;
+ } else {
+ writeln!(w, "{}", indented_decl,)?;
+ }
}
let Some(children) = scope_tree.get(&parent) else {
@@ -627,14 +644,18 @@ fn write_scope_tree(
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_embeddable_string(span),
- )?;
+ if tcx.sess.opts.unstable_opts.mir_include_spans {
+ if let Some(span) = span {
+ writeln!(
+ w,
+ "{0:1$} // at {2}",
+ indented_header,
+ ALIGN,
+ tcx.sess.source_map().span_to_embeddable_string(span),
+ )?;
+ } else {
+ writeln!(w, "{}", indented_header)?;
+ }
} else {
writeln!(w, "{}", indented_header)?;
}
@@ -846,7 +867,7 @@ fn write_allocation_newline(
/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
/// is only one line). Note that your prefix should contain a trailing space as the lines are
/// printed directly after it.
-fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
+pub fn write_allocation_bytes<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
tcx: TyCtxt<'tcx>,
alloc: &Allocation<Prov, Extra, Bytes>,
w: &mut dyn std::fmt::Write,
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 53fd2dd23..613b132ff 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -1,6 +1,6 @@
//! Values computed by queries that use MIR.
-use crate::mir::ConstantKind;
+use crate::mir::interpret::ConstValue;
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::unord::UnordSet;
@@ -9,6 +9,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_index::bit_set::BitMatrix;
use rustc_index::{Idx, IndexVec};
+use rustc_span::symbol::Symbol;
use rustc_span::Span;
use rustc_target::abi::{FieldIdx, VariantIdx};
use smallvec::SmallVec;
@@ -150,6 +151,9 @@ pub struct GeneratorLayout<'tcx> {
/// The type of every local stored inside the generator.
pub field_tys: IndexVec<GeneratorSavedLocal, GeneratorSavedTy<'tcx>>,
+ /// The name for debuginfo.
+ pub field_names: IndexVec<GeneratorSavedLocal, Option<Symbol>>,
+
/// Which of the above fields are in each variant. Note that one field may
/// be stored in multiple variants.
pub variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, GeneratorSavedLocal>>,
@@ -413,7 +417,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
ty::ReVar(vid) => {
let br =
ty::BoundRegion { var: ty::BoundVar::new(vid.index()), kind: ty::BrAnon(None) };
- tcx.mk_re_late_bound(depth, br)
+ ty::Region::new_late_bound(tcx, depth, br)
}
_ => bug!("unexpected region in ClosureOutlivesSubjectTy: {r:?}"),
});
@@ -440,7 +444,7 @@ impl<'tcx> ClosureOutlivesSubjectTy<'tcx> {
#[derive(Copy, Clone, Debug, HashStable)]
pub struct DestructuredConstant<'tcx> {
pub variant: Option<VariantIdx>,
- pub fields: &'tcx [ConstantKind<'tcx>],
+ pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
}
/// Coverage information summarized from a MIR if instrumented for source code coverage (see
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
index 2165403da..730c55157 100644
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ b/compiler/rustc_middle/src/mir/spanview.rs
@@ -3,7 +3,7 @@ use rustc_middle::hir;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::MirSpanview;
-use rustc_span::{BytePos, Pos, Span, SyntaxContext};
+use rustc_span::{BytePos, Pos, Span};
use std::cmp;
use std::io::{self, Write};
@@ -15,8 +15,9 @@ const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT B
const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
const HEADER: &str = r#"<!DOCTYPE html>
-<html>
-<head>"#;
+<html lang="en">
+<head>
+<meta charset="utf-8">"#;
const START_BODY: &str = r#"</head>
<body>"#;
const FOOTER: &str = r#"</body>
@@ -327,7 +328,7 @@ fn compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span {
let mut span = data.terminator().source_info.span;
for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
// Only combine Spans from the root context, and within the function's body_span.
- if statement_span.ctxt() == SyntaxContext::root() && body_span.contains(statement_span) {
+ if statement_span.ctxt().is_root() && body_span.contains(statement_span) {
span = span.to(statement_span);
}
}
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 3e474c1d3..7f1d38203 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -7,7 +7,7 @@ use super::{BasicBlock, Constant, Local, SwitchTargets, UserTypeProjection};
use crate::mir::coverage::{CodeRegion, CoverageKind};
use crate::traits::Reveal;
-use crate::ty::adjustment::PointerCast;
+use crate::ty::adjustment::PointerCoercion;
use crate::ty::subst::SubstsRef;
use crate::ty::{self, List, Ty};
use crate::ty::{Region, UserTypeAnnotationIndex};
@@ -182,6 +182,16 @@ pub enum BorrowKind {
/// We can also report errors with this kind of borrow differently.
Shallow,
+ /// Data is mutable and not aliasable.
+ Mut { kind: MutBorrowKind },
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)]
+#[derive(Hash, HashStable)]
+pub enum MutBorrowKind {
+ Default,
+ /// This borrow arose from method-call auto-ref. (i.e., `adjustment::Adjust::Borrow`)
+ TwoPhaseBorrow,
/// Data must be immutable but not aliasable. This kind of borrow
/// cannot currently be expressed by the user and is used only in
/// implicit closure bindings. It is needed when the closure is
@@ -216,23 +226,14 @@ pub enum BorrowKind {
/// user code, if awkward, but extra weird for closures, since the
/// borrow is hidden.
///
- /// So we introduce a "unique imm" borrow -- the referent is
- /// immutable, but not aliasable. This solves the problem. For
- /// simplicity, we don't give users the way to express this
- /// borrow, it's just used when translating closures.
+ /// So we introduce a `ClosureCapture` borrow -- user will not have to mark the variable
+ /// containing the mutable reference as `mut`, as they didn't ever
+ /// intend to mutate the mutable reference itself. We still mutable capture it in order to
+ /// mutate the pointed value through it (but not mutating the reference itself).
///
- // FIXME(#112072): This is wrong. Unique borrows are mutable borrows except
- // that they do not require their pointee to be marked as a mutable.
- // They should still be treated as mutable borrows in every other way,
- // e.g. for variance or overlap checking.
- Unique,
-
- /// Data is mutable and not aliasable.
- Mut {
- /// `true` if this borrow arose from method-call auto-ref
- /// (i.e., `adjustment::Adjust::Borrow`).
- allow_two_phase_borrow: bool,
- },
+ /// This solves the problem. For simplicity, we don't give users the way to express this
+ /// borrow, it's just used when translating closures.
+ ClosureCapture,
}
///////////////////////////////////////////////////////////////////////////
@@ -512,6 +513,31 @@ pub struct CopyNonOverlapping<'tcx> {
pub count: Operand<'tcx>,
}
+/// Represents how a `TerminatorKind::Call` was constructed, used for diagnostics
+#[derive(Clone, Copy, TyEncodable, TyDecodable, Debug, PartialEq, Hash, HashStable)]
+#[derive(TypeFoldable, TypeVisitable)]
+pub enum CallSource {
+ /// This came from something such as `a > b` or `a + b`. In THIR, if `from_hir_call`
+ /// is false then this is the desugaring.
+ OverloadedOperator,
+ /// This was from comparison generated by a match, used by const-eval for better errors
+ /// when the comparison cannot be done in compile time.
+ ///
+ /// (see <https://github.com/rust-lang/rust/issues/90237>)
+ MatchCmp,
+ /// Other types of desugaring that did not come from the HIR, but we don't care about
+ /// for diagnostics (yet).
+ Misc,
+ /// Normal function call, no special source
+ Normal,
+}
+
+impl CallSource {
+ pub fn from_hir_call(self) -> bool {
+ matches!(self, CallSource::Normal)
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// Terminators
@@ -638,11 +664,10 @@ pub enum TerminatorKind<'tcx> {
target: Option<BasicBlock>,
/// Action to be taken if the call unwinds.
unwind: UnwindAction,
- /// `true` if this is from a call in HIR rather than from an overloaded
- /// operator. True for overloaded function call.
- from_hir_call: bool,
+ /// Where this call came from in HIR/THIR.
+ call_source: CallSource,
/// This `Span` is the span of the function, without the dot and receiver
- /// (e.g. `foo(a, b)` in `x.foo(a, b)`
+ /// e.g. `foo(a, b)` in `x.foo(a, b)`
fn_span: Span,
},
@@ -801,7 +826,8 @@ pub enum UnwindAction {
}
/// Information about an assertion failure.
-#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)]
+#[derive(Clone, Hash, HashStable, PartialEq, Debug)]
+#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub enum AssertKind<O> {
BoundsCheck { len: O, index: O },
Overflow(BinOp, O, O),
@@ -1204,9 +1230,9 @@ pub enum CastKind {
/// An address-to-pointer cast that picks up an exposed provenance.
/// See the docs on `from_exposed_addr` for more details.
PointerFromExposedAddress,
- /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
+ /// Pointer related casts that are done by coercions. Note that reference-to-raw-ptr casts are
/// translated into `&raw mut/const *r`, i.e., they are not actually casts.
- Pointer(PointerCast),
+ PointerCoercion(PointerCoercion),
/// Cast into a dyn* object.
DynStar,
IntToInt,
@@ -1266,19 +1292,30 @@ pub enum UnOp {
pub enum BinOp {
/// The `+` operator (addition)
Add,
+ /// Like `Add`, but with UB on overflow. (Integers only.)
+ AddUnchecked,
/// The `-` operator (subtraction)
Sub,
+ /// Like `Sub`, but with UB on overflow. (Integers only.)
+ SubUnchecked,
/// The `*` operator (multiplication)
Mul,
+ /// Like `Mul`, but with UB on overflow. (Integers only.)
+ MulUnchecked,
/// The `/` operator (division)
///
- /// Division by zero is UB, because the compiler should have inserted checks
- /// prior to this.
+ /// For integer types, division by zero is UB, as is `MIN / -1` for signed.
+ /// The compiler should have inserted checks prior to this.
+ ///
+ /// Floating-point division by zero is safe, and does not need guards.
Div,
/// The `%` operator (modulus)
///
- /// Using zero as the modulus (second operand) is UB, because the compiler
- /// should have inserted checks prior to this.
+ /// For integer types, using zero as the modulus (second operand) is UB,
+ /// as is `MIN % -1` for signed.
+ /// The compiler should have inserted checks prior to this.
+ ///
+ /// Floating-point remainder by zero is safe, and does not need guards.
Rem,
/// The `^` operator (bitwise xor)
BitXor,
@@ -1290,10 +1327,17 @@ pub enum BinOp {
///
/// The offset is truncated to the size of the first operand before shifting.
Shl,
+ /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+ ShlUnchecked,
/// The `>>` operator (shift right)
///
/// The offset is truncated to the size of the first operand before shifting.
+ ///
+ /// This is an arithmetic shift if the LHS is signed
+ /// and a logical shift if the LHS is unsigned.
Shr,
+ /// Like `Shl`, but is UB if the RHS >= LHS::BITS
+ ShrUnchecked,
/// The `==` operator (equality)
Eq,
/// The `<` operator (less than)
diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs
index 5ca824134..8618a5315 100644
--- a/compiler/rustc_middle/src/mir/tcx.rs
+++ b/compiler/rustc_middle/src/mir/tcx.rs
@@ -95,11 +95,13 @@ impl<'tcx> PlaceTy<'tcx> {
ProjectionElem::Subslice { from, to, from_end } => {
PlaceTy::from_ty(match self.ty.kind() {
ty::Slice(..) => self.ty,
- ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64),
+ ty::Array(inner, _) if !from_end => {
+ Ty::new_array(tcx, *inner, (to - from) as u64)
+ }
ty::Array(inner, size) if from_end => {
let size = size.eval_target_usize(tcx, param_env);
let len = size - from - to;
- tcx.mk_array(*inner, len)
+ Ty::new_array(tcx, *inner, len)
}
_ => bug!("cannot subslice non-array type: `{:?}`", self),
})
@@ -162,16 +164,16 @@ impl<'tcx> Rvalue<'tcx> {
match *self {
Rvalue::Use(ref operand) => operand.ty(local_decls, tcx),
Rvalue::Repeat(ref operand, count) => {
- tcx.mk_array_with_const_len(operand.ty(local_decls, tcx), count)
+ Ty::new_array_with_const_len(tcx, operand.ty(local_decls, tcx), count)
}
Rvalue::ThreadLocalRef(did) => tcx.thread_local_ptr_ty(did),
Rvalue::Ref(reg, bk, ref place) => {
let place_ty = place.ty(local_decls, tcx).ty;
- tcx.mk_ref(reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
+ Ty::new_ref(tcx, reg, ty::TypeAndMut { ty: place_ty, mutbl: bk.to_mutbl_lossy() })
}
Rvalue::AddressOf(mutability, ref place) => {
let place_ty = place.ty(local_decls, tcx).ty;
- tcx.mk_ptr(ty::TypeAndMut { ty: place_ty, mutbl: mutability })
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: place_ty, mutbl: mutability })
}
Rvalue::Len(..) => tcx.types.usize,
Rvalue::Cast(.., ty) => ty,
@@ -184,7 +186,7 @@ impl<'tcx> Rvalue<'tcx> {
let lhs_ty = lhs.ty(local_decls, tcx);
let rhs_ty = rhs.ty(local_decls, tcx);
let ty = op.ty(tcx, lhs_ty, rhs_ty);
- tcx.mk_tup(&[ty, tcx.types.bool])
+ Ty::new_tup(tcx, &[ty, tcx.types.bool])
}
Rvalue::UnaryOp(UnOp::Not | UnOp::Neg, ref operand) => operand.ty(local_decls, tcx),
Rvalue::Discriminant(ref place) => place.ty(local_decls, tcx).ty.discriminant_ty(tcx),
@@ -192,17 +194,17 @@ impl<'tcx> Rvalue<'tcx> {
tcx.types.usize
}
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
- AggregateKind::Array(ty) => tcx.mk_array(ty, ops.len() as u64),
+ AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
AggregateKind::Tuple => {
- tcx.mk_tup_from_iter(ops.iter().map(|op| op.ty(local_decls, tcx)))
+ Ty::new_tup_from_iter(tcx, ops.iter().map(|op| op.ty(local_decls, tcx)))
}
AggregateKind::Adt(did, _, substs, _, _) => tcx.type_of(did).subst(tcx, substs),
- AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs),
+ AggregateKind::Closure(did, substs) => Ty::new_closure(tcx, did, substs),
AggregateKind::Generator(did, substs, movability) => {
- tcx.mk_generator(did, substs, movability)
+ Ty::new_generator(tcx, did, substs, movability)
}
},
- Rvalue::ShallowInitBox(_, ty) => tcx.mk_box(ty),
+ Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,
}
}
@@ -235,8 +237,11 @@ impl<'tcx> BinOp {
// FIXME: handle SIMD correctly
match self {
&BinOp::Add
+ | &BinOp::AddUnchecked
| &BinOp::Sub
+ | &BinOp::SubUnchecked
| &BinOp::Mul
+ | &BinOp::MulUnchecked
| &BinOp::Div
| &BinOp::Rem
| &BinOp::BitXor
@@ -246,7 +251,11 @@ impl<'tcx> BinOp {
assert_eq!(lhs_ty, rhs_ty);
lhs_ty
}
- &BinOp::Shl | &BinOp::Shr | &BinOp::Offset => {
+ &BinOp::Shl
+ | &BinOp::ShlUnchecked
+ | &BinOp::Shr
+ | &BinOp::ShrUnchecked
+ | &BinOp::Offset => {
lhs_ty // lhs_ty can be != rhs_ty
}
&BinOp::Eq | &BinOp::Lt | &BinOp::Le | &BinOp::Ne | &BinOp::Ge | &BinOp::Gt => {
@@ -262,11 +271,6 @@ impl BorrowKind {
BorrowKind::Mut { .. } => hir::Mutability::Mut,
BorrowKind::Shared => hir::Mutability::Not,
- // We have no type corresponding to a unique imm borrow, so
- // use `&mut`. It gives all the capabilities of a `&uniq`
- // and hence is a safe "over approximation".
- BorrowKind::Unique => hir::Mutability::Mut,
-
// We have no type corresponding to a shallow borrow, so use
// `&` as an approximation.
BorrowKind::Shallow => hir::Mutability::Not,
@@ -293,7 +297,14 @@ impl BinOp {
BinOp::Gt => hir::BinOpKind::Gt,
BinOp::Le => hir::BinOpKind::Le,
BinOp::Ge => hir::BinOpKind::Ge,
- BinOp::Offset => unreachable!(),
+ BinOp::AddUnchecked
+ | BinOp::SubUnchecked
+ | BinOp::MulUnchecked
+ | BinOp::ShlUnchecked
+ | BinOp::ShrUnchecked
+ | BinOp::Offset => {
+ unreachable!()
+ }
}
}
}
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 2c6126cdd..1b9c1438f 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -105,7 +105,7 @@ pub struct Terminator<'tcx> {
pub kind: TerminatorKind<'tcx>,
}
-pub type Successors<'a> = impl Iterator<Item = BasicBlock> + 'a;
+pub type Successors<'a> = impl DoubleEndedIterator<Item = BasicBlock> + 'a;
pub type SuccessorsMut<'a> =
iter::Chain<std::option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
@@ -272,7 +272,8 @@ impl<'tcx> Debug for TerminatorKind<'tcx> {
let unwind = match self.unwind() {
// Not needed or included in successors
- None | Some(UnwindAction::Continue) | Some(UnwindAction::Cleanup(_)) => None,
+ None | Some(UnwindAction::Cleanup(_)) => None,
+ Some(UnwindAction::Continue) => Some("unwind continue"),
Some(UnwindAction::Unreachable) => Some("unwind unreachable"),
Some(UnwindAction::Terminate) => Some("unwind terminate"),
};
diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs
index 7d247eeb6..ec16a8470 100644
--- a/compiler/rustc_middle/src/mir/traversal.rs
+++ b/compiler/rustc_middle/src/mir/traversal.rs
@@ -149,7 +149,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
// B C
// | |
// | |
- // D |
+ // | D
// \ /
// \ /
// E
@@ -159,26 +159,26 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
//
// When the first call to `traverse_successor` happens, the following happens:
//
- // [(B, [D]), // `B` taken from the successors of `A`, pushed to the
- // // top of the stack along with the successors of `B`
- // (A, [C])]
+ // [(C, [D]), // `C` taken from the successors of `A`, pushed to the
+ // // top of the stack along with the successors of `C`
+ // (A, [B])]
//
- // [(D, [E]), // `D` taken from successors of `B`, pushed to stack
- // (B, []),
- // (A, [C])]
+ // [(D, [E]), // `D` taken from successors of `C`, pushed to stack
+ // (C, []),
+ // (A, [B])]
//
// [(E, []), // `E` taken from successors of `D`, pushed to stack
// (D, []),
- // (B, []),
- // (A, [C])]
+ // (C, []),
+ // (A, [B])]
//
// Now that the top of the stack has no successors we can traverse, each item will
- // be popped off during iteration until we get back to `A`. This yields [E, D, B].
+ // be popped off during iteration until we get back to `A`. This yields [E, D, C].
//
- // When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
+ // When we yield `C` and call `traverse_successor`, we push `B` to the stack, but
// since we've already visited `E`, that child isn't added to the stack. The last
- // two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
- while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next() {
+ // two iterations yield `B` and finally `A` for a final traversal of [E, D, C, B, A]
+ while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next_back() {
if self.visited.insert(bb) {
if let Some(term) = &self.basic_blocks[bb].terminator {
self.visit_stack.push((bb, term.successors()));
@@ -188,10 +188,6 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
}
}
-pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> {
- Postorder::new(&body.basic_blocks, START_BLOCK)
-}
-
impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
@@ -219,6 +215,17 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
}
}
+/// Creates an iterator over the `Body`'s basic blocks, that:
+/// - returns basic blocks in a postorder,
+/// - traverses the `BasicBlocks` CFG cache's reverse postorder backwards, and does not cache the
+/// postorder itself.
+pub fn postorder<'a, 'tcx>(
+ body: &'a Body<'tcx>,
+) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
+{
+ reverse_postorder(body).rev()
+}
+
/// Reverse postorder traversal of a graph
///
/// Reverse postorder is the reverse order of a postorder traversal.
@@ -295,34 +302,12 @@ pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
iter.visited
}
-#[derive(Clone)]
-pub struct ReversePostorderIter<'a, 'tcx> {
+/// Creates an iterator over the `Body`'s basic blocks, that:
+/// - returns basic blocks in a reverse postorder,
+/// - makes use of the `BasicBlocks` CFG cache's reverse postorder.
+pub fn reverse_postorder<'a, 'tcx>(
body: &'a Body<'tcx>,
- blocks: &'a [BasicBlock],
- idx: usize,
-}
-
-impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
- type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
-
- fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
- if self.idx == 0 {
- return None;
- }
- self.idx -= 1;
-
- self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- (self.idx, Some(self.idx))
- }
-}
-
-impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
-
-pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
- let blocks = body.basic_blocks.postorder();
- let len = blocks.len();
- ReversePostorderIter { body, blocks, idx: len }
+) -> impl Iterator<Item = (BasicBlock, &'a BasicBlockData<'tcx>)> + ExactSizeIterator + DoubleEndedIterator
+{
+ body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
}
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 8d44e929a..205dc9ec7 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -519,7 +519,7 @@ macro_rules! make_mir_visitor {
destination,
target: _,
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _
} => {
self.visit_operand(func, location);
@@ -650,9 +650,6 @@ macro_rules! make_mir_visitor {
BorrowKind::Shallow => PlaceContext::NonMutatingUse(
NonMutatingUseContext::ShallowBorrow
),
- BorrowKind::Unique => PlaceContext::MutatingUse(
- MutatingUseContext::Borrow
- ),
BorrowKind::Mut { .. } =>
PlaceContext::MutatingUse(MutatingUseContext::Borrow),
};
@@ -1136,13 +1133,12 @@ macro_rules! visit_place_fns {
fn visit_projection_elem(
&mut self,
- local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
- self.super_projection_elem(local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
}
fn super_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
@@ -1171,15 +1167,13 @@ macro_rules! visit_place_fns {
location: Location,
) {
for (base, elem) in place_ref.iter_projections().rev() {
- let base_proj = base.projection;
- self.visit_projection_elem(place_ref.local, base_proj, elem, context, location);
+ self.visit_projection_elem(base, elem, context, location);
}
}
fn super_projection_elem(
&mut self,
- _local: Local,
- _proj_base: &[PlaceElem<'tcx>],
+ _place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
_context: PlaceContext,
location: Location,
diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index fd02a1613..2c481745d 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -55,6 +55,10 @@ impl<T> EraseType for &'_ ty::List<T> {
type Result = [u8; size_of::<*const ()>()];
}
+impl<I: rustc_index::Idx, T> EraseType for &'_ rustc_index::IndexSlice<I, T> {
+ type Result = [u8; size_of::<&'static rustc_index::IndexSlice<u32, ()>>()];
+}
+
impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
}
@@ -67,8 +71,8 @@ impl<T> EraseType for Result<&'_ T, traits::CodegenObligationError> {
type Result = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()];
}
-impl<T> EraseType for Result<&'_ T, ty::layout::FnAbiError<'_>> {
- type Result = [u8; size_of::<Result<&'static (), ty::layout::FnAbiError<'static>>>()];
+impl<T> EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
+ type Result = [u8; size_of::<Result<&'static (), &'static ty::layout::FnAbiError<'static>>>()];
}
impl<T> EraseType for Result<(&'_ T, rustc_middle::thir::ExprId), rustc_errors::ErrorGuaranteed> {
@@ -92,15 +96,17 @@ impl EraseType for Result<ty::GenericArg<'_>, traits::query::NoSolution> {
type Result = [u8; size_of::<Result<ty::GenericArg<'static>, traits::query::NoSolution>>()];
}
-impl EraseType for Result<bool, ty::layout::LayoutError<'_>> {
- type Result = [u8; size_of::<Result<bool, ty::layout::LayoutError<'static>>>()];
+impl EraseType for Result<bool, &ty::layout::LayoutError<'_>> {
+ type Result = [u8; size_of::<Result<bool, &'static ty::layout::LayoutError<'static>>>()];
}
-impl EraseType for Result<rustc_target::abi::TyAndLayout<'_, Ty<'_>>, ty::layout::LayoutError<'_>> {
+impl EraseType
+ for Result<rustc_target::abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>>
+{
type Result = [u8; size_of::<
Result<
rustc_target::abi::TyAndLayout<'static, Ty<'static>>,
- ty::layout::LayoutError<'static>,
+ &'static ty::layout::LayoutError<'static>,
>,
>()];
}
@@ -309,7 +315,6 @@ tcx_lifetime! {
rustc_middle::mir::interpret::ConstValue,
rustc_middle::mir::interpret::GlobalId,
rustc_middle::mir::interpret::LitToConstInput,
- rustc_middle::traits::ChalkEnvironmentAndGoal,
rustc_middle::traits::query::MethodAutoderefStepsResult,
rustc_middle::traits::query::type_op::AscribeUserType,
rustc_middle::traits::query::type_op::Eq,
@@ -317,7 +322,7 @@ tcx_lifetime! {
rustc_middle::traits::query::type_op::Subtype,
rustc_middle::ty::AdtDef,
rustc_middle::ty::AliasTy,
- rustc_middle::ty::Clause,
+ rustc_middle::ty::ClauseKind,
rustc_middle::ty::ClosureTypeInfo,
rustc_middle::ty::Const,
rustc_middle::ty::DestructuredConst,
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index fa62b7f32..28e699cd2 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -2,6 +2,7 @@
use crate::infer::canonical::Canonical;
use crate::mir;
+use crate::mir::interpret::ConstValue;
use crate::traits;
use crate::ty::fast_reject::SimplifiedType;
use crate::ty::layout::{TyAndLayout, ValidityRequirement};
@@ -317,11 +318,11 @@ impl<'tcx> Key for (LocalDefId, DefId, SubstsRef<'tcx>) {
}
}
-impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
+impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>) {
type CacheSelector = DefaultCacheSelector<Self>;
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
- tcx.def_span(self.1.def_id())
+ tcx.def_span(self.1.def_id)
}
}
@@ -333,6 +334,14 @@ impl<'tcx> Key for (ty::Const<'tcx>, FieldIdx) {
}
}
+impl<'tcx> Key for (ConstValue<'tcx>, Ty<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
+ fn default_span(&self, _: TyCtxt<'_>) -> Span {
+ DUMMY_SP
+ }
+}
+
impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
type CacheSelector = DefaultCacheSelector<Self>;
@@ -420,7 +429,7 @@ impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
}
}
-impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> {
+impl<'tcx> Key for &'tcx ty::List<ty::Clause<'tcx>> {
type CacheSelector = DefaultCacheSelector<Self>;
fn default_span(&self, _: TyCtxt<'_>) -> Span {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 1528be42f..a059590e6 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -39,8 +39,8 @@ use crate::traits::query::{
};
use crate::traits::specialization_graph;
use crate::traits::{
- CanonicalChalkEnvironmentAndGoal, CodegenObligationError, EvaluationResult, ImplSource,
- ObjectSafetyViolation, ObligationCause, OverflowError, WellFormedLoc,
+ CodegenObligationError, EvaluationResult, ImplSource, ObjectSafetyViolation, ObligationCause,
+ OverflowError, WellFormedLoc,
};
use crate::ty::fast_reject::SimplifiedType;
use crate::ty::layout::ValidityRequirement;
@@ -54,7 +54,7 @@ use crate::ty::{
};
use rustc_arena::TypedArena;
use rustc_ast as ast;
-use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_ast::expand::{allocator::AllocatorKind, StrippedCfgItem};
use rustc_attr as attr;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
@@ -346,7 +346,7 @@ rustc_queries! {
/// `key` is the `DefId` of the associated type or opaque type.
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
- query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Predicate<'tcx>, Span)]> {
+ query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx [(ty::Clause<'tcx>, Span)]> {
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
separate_provide_extern
@@ -373,7 +373,7 @@ rustc_queries! {
/// ```
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
- query item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
+ query item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Clause<'tcx>>> {
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
}
@@ -531,6 +531,19 @@ rustc_queries! {
}
}
+ /// Returns names of captured upvars for closures and generators.
+ ///
+ /// Here are some examples:
+ /// - `name__field1__field2` when the upvar is captured by value.
+ /// - `_ref__name__field` when the upvar is captured by reference.
+ ///
+ /// For generators this only contains upvars that are shared by all states.
+ query closure_saved_names_of_captured_variables(def_id: DefId) -> &'tcx IndexVec<abi::FieldIdx, Symbol> {
+ arena_cache
+ desc { |tcx| "computing debuginfo for closure `{}`", tcx.def_path_str(def_id) }
+ separate_provide_extern
+ }
+
query mir_generator_witnesses(key: DefId) -> &'tcx Option<mir::GeneratorLayout<'tcx>> {
arena_cache
desc { |tcx| "generator witness types for `{}`", tcx.def_path_str(key) }
@@ -868,7 +881,7 @@ rustc_queries! {
///
/// Note that we've liberated the late bound regions of function signatures, so
/// this can not be used to check whether these types are well formed.
- query assumed_wf_types(key: DefId) -> &'tcx ty::List<Ty<'tcx>> {
+ query assumed_wf_types(key: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
desc { |tcx| "computing the implied bounds of `{}`", tcx.def_path_str(key) }
}
@@ -1074,19 +1087,13 @@ rustc_queries! {
}
/// Tries to destructure an `mir::ConstantKind` ADT or array into its variant index
- /// and its field values.
- query try_destructure_mir_constant(
- key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
+ /// and its field values. This should only be used for pretty printing.
+ query try_destructure_mir_constant_for_diagnostics(
+ key: (ConstValue<'tcx>, Ty<'tcx>)
) -> Option<mir::DestructuredConstant<'tcx>> {
desc { "destructuring MIR constant"}
- }
-
- /// Dereference a constant reference or raw pointer and turn the result into a constant
- /// again.
- query deref_mir_constant(
- key: ty::ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
- ) -> mir::ConstantKind<'tcx> {
- desc { "dereferencing MIR constant" }
+ no_hash
+ eval_always
}
query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
@@ -1100,10 +1107,6 @@ rustc_queries! {
desc { "converting literal to const" }
}
- query lit_to_mir_constant(key: LitToConstInput<'tcx>) -> Result<mir::ConstantKind<'tcx>, LitToConstError> {
- desc { "converting literal to mir constant" }
- }
-
query check_match(key: LocalDefId) -> Result<(), rustc_errors::ErrorGuaranteed> {
desc { |tcx| "match-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { true }
@@ -1275,7 +1278,7 @@ rustc_queries! {
}
query codegen_select_candidate(
- key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
+ key: (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>)
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
cache_on_disk_if { true }
desc { |tcx| "computing candidate for `{}`", key.1 }
@@ -1382,7 +1385,7 @@ rustc_queries! {
/// executes in "reveal all" mode, and will normalize the input type.
query layout_of(
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
- ) -> Result<ty::layout::TyAndLayout<'tcx>, ty::layout::LayoutError<'tcx>> {
+ ) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
depth_limit
desc { "computing layout of `{}`", key.value }
}
@@ -1393,7 +1396,7 @@ rustc_queries! {
/// instead, where the instance is an `InstanceDef::Virtual`.
query fn_abi_of_fn_ptr(
key: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>
- ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> {
+ ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> {
desc { "computing call ABI of `{}` function pointers", key.value.0 }
}
@@ -1404,7 +1407,7 @@ rustc_queries! {
/// to an `InstanceDef::Virtual` instance (of `<dyn Trait as Trait>::fn`).
query fn_abi_of_instance(
key: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>)>
- ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, ty::layout::FnAbiError<'tcx>> {
+ ) -> Result<&'tcx abi::call::FnAbi<'tcx, Ty<'tcx>>, &'tcx ty::layout::FnAbiError<'tcx>> {
desc { "computing call ABI of `{}`", key.value.0 }
}
@@ -1487,8 +1490,9 @@ rustc_queries! {
desc { "getting traits in scope at a block" }
}
- query impl_defaultness(def_id: DefId) -> hir::Defaultness {
- desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) }
+ /// Returns whether the impl or associated function has the `default` keyword.
+ query defaultness(def_id: DefId) -> hir::Defaultness {
+ desc { |tcx| "looking up whether `{}` has `default`", tcx.def_path_str(def_id) }
separate_provide_extern
feedable
}
@@ -1915,6 +1919,16 @@ rustc_queries! {
}
/// Do not call this query directly: invoke `normalize` instead.
+ query normalize_weak_ty(
+ goal: CanonicalProjectionGoal<'tcx>
+ ) -> Result<
+ &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
+ NoSolution,
+ > {
+ desc { "normalizing `{}`", goal.value.value }
+ }
+
+ /// Do not call this query directly: invoke `normalize` instead.
query normalize_inherent_projection_ty(
goal: CanonicalProjectionGoal<'tcx>
) -> Result<
@@ -1959,15 +1973,6 @@ rustc_queries! {
desc { "evaluating trait selection obligation `{}`", goal.value.value }
}
- query evaluate_goal(
- goal: CanonicalChalkEnvironmentAndGoal<'tcx>
- ) -> Result<
- &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
- NoSolution
- > {
- desc { "evaluating trait selection obligation `{}`", goal.value }
- }
-
/// Do not call this query directly: part of the `Eq` type-op
query type_op_ascribe_user_type(
goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx>
@@ -2019,10 +2024,10 @@ rustc_queries! {
}
/// Do not call this query directly: part of the `Normalize` type-op
- query type_op_normalize_predicate(
- goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Predicate<'tcx>>
+ query type_op_normalize_clause(
+ goal: CanonicalTypeOpNormalizeGoal<'tcx, ty::Clause<'tcx>>
) -> Result<
- &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>,
+ &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Clause<'tcx>>>,
NoSolution,
> {
desc { "normalizing `{:?}`", goal.value.value.value }
@@ -2113,7 +2118,7 @@ rustc_queries! {
desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) }
}
- query reveal_opaque_types_in_bounds(key: &'tcx ty::List<ty::Predicate<'tcx>>) -> &'tcx ty::List<ty::Predicate<'tcx>> {
+ query reveal_opaque_types_in_bounds(key: &'tcx ty::List<ty::Clause<'tcx>>) -> &'tcx ty::List<ty::Clause<'tcx>> {
desc { "revealing opaque types in `{:?}`", key }
}
@@ -2152,7 +2157,7 @@ rustc_queries! {
separate_provide_extern
}
- query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, ty::layout::LayoutError<'tcx>> {
+ query check_validity_requirement(key: (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>)) -> Result<bool, &'tcx ty::layout::LayoutError<'tcx>> {
desc { "checking validity requirement for `{}`: {}", key.1.value, key.0 }
}
@@ -2185,6 +2190,19 @@ rustc_queries! {
query check_tys_might_be_eq(arg: Canonical<'tcx, (ty::ParamEnv<'tcx>, Ty<'tcx>, Ty<'tcx>)>) -> Result<(), NoSolution> {
desc { "check whether two const param are definitely not equal to eachother"}
}
+
+ /// Get all item paths that were stripped by a `#[cfg]` in a particular crate.
+ /// Should not be called for the local crate before the resolver outputs are created, as it
+ /// is only fed there.
+ query stripped_cfg_items(cnum: CrateNum) -> &'tcx [StrippedCfgItem] {
+ feedable
+ desc { "getting cfg-ed out item names" }
+ separate_provide_extern
+ }
+
+ query generics_require_sized_self(def_id: DefId) -> bool {
+ desc { "check whether the item has a `where Self: Sized` bound" }
+ }
}
rustc_query_append! { define_callbacks! }
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 220118ae5..8751d3b78 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -791,13 +791,6 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
}
}
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Predicate<'tcx>, Span)] {
- #[inline]
- fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
- RefDecodable::decode(d)
- }
-}
-
impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx [(ty::Clause<'tcx>, Span)] {
#[inline]
fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 813e109c4..e9af5070e 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -18,7 +18,7 @@ use rustc_index::IndexVec;
use rustc_middle::middle::region;
use rustc_middle::mir::interpret::AllocId;
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Mutability, UnOp};
-use rustc_middle::ty::adjustment::PointerCast;
+use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtDef, FnSig, List, Ty, UpvarSubsts};
use rustc_middle::ty::{CanonicalUserType, CanonicalUserTypeAnnotation};
@@ -329,9 +329,10 @@ pub enum ExprKind<'tcx> {
NeverToAny {
source: ExprId,
},
- /// A pointer cast. More information can be found in [`PointerCast`].
- Pointer {
- cast: PointerCast,
+ /// A pointer coercion. More information can be found in [`PointerCoercion`].
+ /// Pointer casts that cannot be done by coercions are represented by [`ExprKind::Cast`].
+ PointerCoercion {
+ cast: PointerCoercion,
source: ExprId,
},
/// A `loop` expression.
@@ -410,6 +411,10 @@ pub enum ExprKind<'tcx> {
Return {
value: Option<ExprId>,
},
+ /// A `become` expression.
+ Become {
+ value: ExprId,
+ },
/// An inline `const` block, e.g. `const {}`.
ConstBlock {
did: DefId,
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index 5c7ec31cf..14bc1ac0c 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -65,7 +65,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
Cast { source } => visitor.visit_expr(&visitor.thir()[source]),
Use { source } => visitor.visit_expr(&visitor.thir()[source]),
NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
- Pointer { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
+ PointerCoercion { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
Let { expr, .. } => {
visitor.visit_expr(&visitor.thir()[expr]);
}
@@ -100,6 +100,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
visitor.visit_expr(&visitor.thir()[value])
}
}
+ Become { value } => visitor.visit_expr(&visitor.thir()[value]),
ConstBlock { did: _, substs: _ } => {}
Repeat { value, count: _ } => {
visitor.visit_expr(&visitor.thir()[value]);
diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs
deleted file mode 100644
index fcc8f457a..000000000
--- a/compiler/rustc_middle/src/traits/chalk.rs
+++ /dev/null
@@ -1,396 +0,0 @@
-//! Types required for Chalk-related queries
-//!
-//! The primary purpose of this file is defining an implementation for the
-//! `chalk_ir::interner::Interner` trait. The primary purpose of this trait, as
-//! its name suggest, is to provide an abstraction boundary for creating
-//! interned Chalk types.
-
-use rustc_middle::ty::{self, AdtDef, TyCtxt};
-
-use rustc_hir::def_id::DefId;
-use rustc_target::spec::abi::Abi;
-
-use std::cmp::Ordering;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-
-#[derive(Copy, Clone)]
-pub struct RustInterner<'tcx> {
- pub tcx: TyCtxt<'tcx>,
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Hash for RustInterner<'tcx> {
- fn hash<H: Hasher>(&self, _state: &mut H) {}
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Ord for RustInterner<'tcx> {
- fn cmp(&self, _other: &Self) -> Ordering {
- Ordering::Equal
- }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> PartialOrd for RustInterner<'tcx> {
- fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
- None
- }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> PartialEq for RustInterner<'tcx> {
- fn eq(&self, _other: &Self) -> bool {
- false
- }
-}
-
-/// We don't ever actually need this. It's only required for derives.
-impl<'tcx> Eq for RustInterner<'tcx> {}
-
-impl fmt::Debug for RustInterner<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "RustInterner")
- }
-}
-
-// Right now, there is no interning at all. I was running into problems with
-// adding interning in `ty/context.rs` for Chalk types with
-// `parallel-compiler = true`. -jackh726
-impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> {
- type InternedType = Box<chalk_ir::TyData<Self>>;
- type InternedLifetime = Box<chalk_ir::LifetimeData<Self>>;
- type InternedConst = Box<chalk_ir::ConstData<Self>>;
- type InternedConcreteConst = ty::ValTree<'tcx>;
- type InternedGenericArg = Box<chalk_ir::GenericArgData<Self>>;
- type InternedGoal = Box<chalk_ir::GoalData<Self>>;
- type InternedGoals = Vec<chalk_ir::Goal<Self>>;
- type InternedSubstitution = Vec<chalk_ir::GenericArg<Self>>;
- type InternedProgramClause = Box<chalk_ir::ProgramClauseData<Self>>;
- type InternedProgramClauses = Vec<chalk_ir::ProgramClause<Self>>;
- type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
- type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>;
- type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>;
- type InternedVariances = Vec<chalk_ir::Variance>;
- type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>;
- type DefId = DefId;
- type InternedAdtId = AdtDef<'tcx>;
- type Identifier = ();
- type FnAbi = Abi;
-
- fn debug_program_clause_implication(
- pci: &chalk_ir::ProgramClauseImplication<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- let mut write = || {
- write!(fmt, "{:?}", pci.consequence)?;
-
- let conditions = pci.conditions.interned();
- let constraints = pci.constraints.interned();
-
- let conds = conditions.len();
- let consts = constraints.len();
- if conds == 0 && consts == 0 {
- return Ok(());
- }
-
- write!(fmt, " :- ")?;
-
- if conds != 0 {
- for cond in &conditions[..conds - 1] {
- write!(fmt, "{:?}, ", cond)?;
- }
- write!(fmt, "{:?}", conditions[conds - 1])?;
- }
-
- if conds != 0 && consts != 0 {
- write!(fmt, " ; ")?;
- }
-
- if consts != 0 {
- for constraint in &constraints[..consts - 1] {
- write!(fmt, "{:?}, ", constraint)?;
- }
- write!(fmt, "{:?}", constraints[consts - 1])?;
- }
-
- Ok(())
- };
- Some(write())
- }
-
- fn debug_substitution(
- substitution: &chalk_ir::Substitution<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", substitution.interned()))
- }
-
- fn debug_separator_trait_ref(
- separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- let substitution = &separator_trait_ref.trait_ref.substitution;
- let parameters = substitution.interned();
- Some(write!(
- fmt,
- "{:?}{}{:?}{:?}",
- parameters[0],
- separator_trait_ref.separator,
- separator_trait_ref.trait_ref.trait_id,
- chalk_ir::debug::Angle(&parameters[1..])
- ))
- }
-
- fn debug_quantified_where_clauses(
- clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", clauses.interned()))
- }
-
- fn debug_ty(ty: &chalk_ir::Ty<Self>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
- 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(
- try {
- 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<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- match alias_ty {
- chalk_ir::AliasTy::Projection(projection_ty) => {
- Self::debug_projection_ty(projection_ty, fmt)
- }
- chalk_ir::AliasTy::Opaque(opaque_ty) => Self::debug_opaque_ty(opaque_ty, fmt),
- }
- }
-
- fn debug_projection_ty(
- projection_ty: &chalk_ir::ProjectionTy<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(
- fmt,
- "projection: {:?} {:?}",
- projection_ty.associated_ty_id, projection_ty.substitution,
- ))
- }
-
- fn debug_opaque_ty(
- opaque_ty: &chalk_ir::OpaqueTy<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
- }
-
- fn intern_ty(self, ty: chalk_ir::TyKind<Self>) -> Self::InternedType {
- let flags = ty.compute_flags(self);
- Box::new(chalk_ir::TyData { kind: ty, flags: flags })
- }
-
- fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData<Self> {
- ty
- }
-
- fn intern_lifetime(self, lifetime: chalk_ir::LifetimeData<Self>) -> Self::InternedLifetime {
- Box::new(lifetime)
- }
-
- fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData<Self> {
- &lifetime
- }
-
- fn intern_const(self, constant: chalk_ir::ConstData<Self>) -> Self::InternedConst {
- Box::new(constant)
- }
-
- fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData<Self> {
- &constant
- }
-
- fn const_eq(
- self,
- _ty: &Self::InternedType,
- c1: &Self::InternedConcreteConst,
- c2: &Self::InternedConcreteConst,
- ) -> bool {
- c1 == c2
- }
-
- fn intern_generic_arg(self, data: chalk_ir::GenericArgData<Self>) -> Self::InternedGenericArg {
- Box::new(data)
- }
-
- fn generic_arg_data(self, data: &Self::InternedGenericArg) -> &chalk_ir::GenericArgData<Self> {
- &data
- }
-
- fn intern_goal(self, goal: chalk_ir::GoalData<Self>) -> Self::InternedGoal {
- Box::new(goal)
- }
-
- fn goal_data(self, goal: &Self::InternedGoal) -> &chalk_ir::GoalData<Self> {
- &goal
- }
-
- fn intern_goals<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::Goal<Self>, E>>,
- ) -> Result<Self::InternedGoals, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn goals_data(self, goals: &Self::InternedGoals) -> &[chalk_ir::Goal<Self>] {
- goals
- }
-
- fn intern_substitution<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::GenericArg<Self>, E>>,
- ) -> Result<Self::InternedSubstitution, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn substitution_data(
- self,
- substitution: &Self::InternedSubstitution,
- ) -> &[chalk_ir::GenericArg<Self>] {
- substitution
- }
-
- fn intern_program_clause(
- self,
- data: chalk_ir::ProgramClauseData<Self>,
- ) -> Self::InternedProgramClause {
- Box::new(data)
- }
-
- fn program_clause_data(
- self,
- clause: &Self::InternedProgramClause,
- ) -> &chalk_ir::ProgramClauseData<Self> {
- &clause
- }
-
- fn intern_program_clauses<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::ProgramClause<Self>, E>>,
- ) -> Result<Self::InternedProgramClauses, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn program_clauses_data(
- self,
- clauses: &Self::InternedProgramClauses,
- ) -> &[chalk_ir::ProgramClause<Self>] {
- clauses
- }
-
- fn intern_quantified_where_clauses<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::QuantifiedWhereClause<Self>, E>>,
- ) -> Result<Self::InternedQuantifiedWhereClauses, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn quantified_where_clauses_data(
- self,
- clauses: &Self::InternedQuantifiedWhereClauses,
- ) -> &[chalk_ir::QuantifiedWhereClause<Self>] {
- clauses
- }
-
- fn intern_generic_arg_kinds<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::VariableKind<Self>, E>>,
- ) -> Result<Self::InternedVariableKinds, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn variable_kinds_data(
- self,
- parameter_kinds: &Self::InternedVariableKinds,
- ) -> &[chalk_ir::VariableKind<Self>] {
- parameter_kinds
- }
-
- fn intern_canonical_var_kinds<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::CanonicalVarKind<Self>, E>>,
- ) -> Result<Self::InternedCanonicalVarKinds, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn canonical_var_kinds_data(
- self,
- canonical_var_kinds: &Self::InternedCanonicalVarKinds,
- ) -> &[chalk_ir::CanonicalVarKind<Self>] {
- canonical_var_kinds
- }
-
- fn intern_constraints<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>,
- ) -> Result<Self::InternedConstraints, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn constraints_data(
- self,
- constraints: &Self::InternedConstraints,
- ) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
- constraints
- }
-
- fn intern_variances<E>(
- self,
- data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>,
- ) -> Result<Self::InternedVariances, E> {
- data.into_iter().collect::<Result<Vec<_>, _>>()
- }
-
- fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] {
- variances
- }
-}
-
-impl<'tcx> chalk_ir::interner::HasInterner for RustInterner<'tcx> {
- type Interner = Self;
-}
-
-/// A chalk environment and goal.
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable, TypeFoldable, TypeVisitable)]
-pub struct ChalkEnvironmentAndGoal<'tcx> {
- pub environment: &'tcx ty::List<ty::Predicate<'tcx>>,
- pub goal: ty::Predicate<'tcx>,
-}
-
-impl<'tcx> fmt::Display for ChalkEnvironmentAndGoal<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "environment: {:?}, goal: {}", self.environment, self.goal)
- }
-}
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 0a903a769..c7d2e4c22 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -2,7 +2,6 @@
//!
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
-mod chalk;
pub mod query;
pub mod select;
pub mod solve;
@@ -30,12 +29,8 @@ use std::hash::{Hash, Hasher};
pub use self::select::{EvaluationCache, EvaluationResult, OverflowError, SelectionCache};
-pub type CanonicalChalkEnvironmentAndGoal<'tcx> = Canonical<'tcx, ChalkEnvironmentAndGoal<'tcx>>;
-
pub use self::ObligationCauseCode::*;
-pub use self::chalk::{ChalkEnvironmentAndGoal, RustInterner as ChalkRustInterner};
-
/// Depending on the stage of compilation, we want projection to be
/// more or less conservative.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable, Encodable, Decodable)]
@@ -445,6 +440,12 @@ pub enum ObligationCauseCode<'tcx> {
/// Obligations to prove that a `std::ops::Drop` impl is not stronger than
/// the ADT it's being implemented for.
DropImpl,
+
+ /// Requirement for a `const N: Ty` to implement `Ty: ConstParamTy`
+ ConstParam(Ty<'tcx>),
+
+ /// Obligations emitted during the normalization of a weak type alias.
+ TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId),
}
/// The 'location' at which we try to perform HIR-based wf checking.
@@ -587,6 +588,10 @@ pub enum SelectionError<'tcx> {
/// Signaling that an error has already been emitted, to avoid
/// multiple errors being shown.
ErrorReporting,
+ /// Computing an opaque type's hidden type caused an error (e.g. a cycle error).
+ /// We can thus not know whether the hidden type implements an auto trait, so
+ /// we should not presume anything about it.
+ OpaqueTypeAutoTraitLeakageUnknown(DefId),
}
#[derive(Clone, Debug, TypeVisitable, Lift)]
@@ -640,12 +645,6 @@ pub enum ImplSource<'tcx, N> {
/// ImplSource identifying a particular impl.
UserDefined(ImplSourceUserDefinedData<'tcx, N>),
- /// ImplSource for auto trait implementations.
- /// This carries the information and nested obligations with regards
- /// to an auto implementation for a trait `Trait`. The nested obligations
- /// ensure the trait implementation holds for all the constituent types.
- AutoImpl(ImplSourceAutoImplData<N>),
-
/// Successful resolution to an obligation provided by the caller
/// for some type parameter. The `Vec<N>` represents the
/// obligations incurred from normalizing the where-clause (if
@@ -653,84 +652,40 @@ pub enum ImplSource<'tcx, N> {
Param(Vec<N>, ty::BoundConstness),
/// Virtual calls through an object.
- Object(ImplSourceObjectData<'tcx, N>),
+ Object(ImplSourceObjectData<N>),
/// Successful resolution for a builtin trait.
- Builtin(ImplSourceBuiltinData<N>),
+ Builtin(Vec<N>),
/// ImplSource for trait upcasting coercion
- TraitUpcasting(ImplSourceTraitUpcastingData<'tcx, N>),
-
- /// ImplSource automatically generated for a closure. The `DefId` is the ID
- /// of the closure expression. This is an `ImplSource::UserDefined` in spirit, but the
- /// impl is generated by the compiler and does not appear in the source.
- Closure(ImplSourceClosureData<'tcx, N>),
-
- /// Same as above, but for a function pointer type with the given signature.
- FnPointer(ImplSourceFnPointerData<'tcx, N>),
-
- /// ImplSource automatically generated for a generator.
- Generator(ImplSourceGeneratorData<'tcx, N>),
-
- /// ImplSource automatically generated for a generator backing an async future.
- Future(ImplSourceFutureData<'tcx, N>),
-
- /// ImplSource for a trait alias.
- TraitAlias(ImplSourceTraitAliasData<'tcx, N>),
-
- /// ImplSource for a `const Drop` implementation.
- ConstDestruct(ImplSourceConstDestructData<N>),
+ TraitUpcasting(ImplSourceTraitUpcastingData<N>),
}
impl<'tcx, N> ImplSource<'tcx, N> {
pub fn nested_obligations(self) -> Vec<N> {
match self {
ImplSource::UserDefined(i) => i.nested,
- ImplSource::Param(n, _) => n,
- ImplSource::Builtin(i) => i.nested,
- ImplSource::AutoImpl(d) => d.nested,
- ImplSource::Closure(c) => c.nested,
- ImplSource::Generator(c) => c.nested,
- ImplSource::Future(c) => c.nested,
+ ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
ImplSource::Object(d) => d.nested,
- ImplSource::FnPointer(d) => d.nested,
- ImplSource::TraitAlias(d) => d.nested,
ImplSource::TraitUpcasting(d) => d.nested,
- ImplSource::ConstDestruct(i) => i.nested,
}
}
pub fn borrow_nested_obligations(&self) -> &[N] {
match self {
ImplSource::UserDefined(i) => &i.nested,
- ImplSource::Param(n, _) => n,
- ImplSource::Builtin(i) => &i.nested,
- ImplSource::AutoImpl(d) => &d.nested,
- ImplSource::Closure(c) => &c.nested,
- ImplSource::Generator(c) => &c.nested,
- ImplSource::Future(c) => &c.nested,
+ ImplSource::Param(n, _) | ImplSource::Builtin(n) => &n,
ImplSource::Object(d) => &d.nested,
- ImplSource::FnPointer(d) => &d.nested,
- ImplSource::TraitAlias(d) => &d.nested,
ImplSource::TraitUpcasting(d) => &d.nested,
- ImplSource::ConstDestruct(i) => &i.nested,
}
}
pub fn borrow_nested_obligations_mut(&mut self) -> &mut [N] {
match self {
ImplSource::UserDefined(i) => &mut i.nested,
- ImplSource::Param(n, _) => n,
- ImplSource::Builtin(i) => &mut i.nested,
- ImplSource::AutoImpl(d) => &mut d.nested,
- ImplSource::Closure(c) => &mut c.nested,
- ImplSource::Generator(c) => &mut c.nested,
- ImplSource::Future(c) => &mut c.nested,
+ ImplSource::Param(n, _) | ImplSource::Builtin(n) => n,
ImplSource::Object(d) => &mut d.nested,
- ImplSource::FnPointer(d) => &mut d.nested,
- ImplSource::TraitAlias(d) => &mut d.nested,
ImplSource::TraitUpcasting(d) => &mut d.nested,
- ImplSource::ConstDestruct(i) => &mut i.nested,
}
}
@@ -745,54 +700,17 @@ impl<'tcx, N> ImplSource<'tcx, N> {
nested: i.nested.into_iter().map(f).collect(),
}),
ImplSource::Param(n, ct) => ImplSource::Param(n.into_iter().map(f).collect(), ct),
- ImplSource::Builtin(i) => ImplSource::Builtin(ImplSourceBuiltinData {
- nested: i.nested.into_iter().map(f).collect(),
- }),
+ ImplSource::Builtin(n) => ImplSource::Builtin(n.into_iter().map(f).collect()),
ImplSource::Object(o) => ImplSource::Object(ImplSourceObjectData {
- upcast_trait_ref: o.upcast_trait_ref,
vtable_base: o.vtable_base,
nested: o.nested.into_iter().map(f).collect(),
}),
- ImplSource::AutoImpl(d) => ImplSource::AutoImpl(ImplSourceAutoImplData {
- trait_def_id: d.trait_def_id,
- nested: d.nested.into_iter().map(f).collect(),
- }),
- ImplSource::Closure(c) => ImplSource::Closure(ImplSourceClosureData {
- closure_def_id: c.closure_def_id,
- substs: c.substs,
- nested: c.nested.into_iter().map(f).collect(),
- }),
- ImplSource::Generator(c) => ImplSource::Generator(ImplSourceGeneratorData {
- generator_def_id: c.generator_def_id,
- substs: c.substs,
- nested: c.nested.into_iter().map(f).collect(),
- }),
- ImplSource::Future(c) => ImplSource::Future(ImplSourceFutureData {
- generator_def_id: c.generator_def_id,
- substs: c.substs,
- nested: c.nested.into_iter().map(f).collect(),
- }),
- ImplSource::FnPointer(p) => ImplSource::FnPointer(ImplSourceFnPointerData {
- fn_ty: p.fn_ty,
- nested: p.nested.into_iter().map(f).collect(),
- }),
- ImplSource::TraitAlias(d) => ImplSource::TraitAlias(ImplSourceTraitAliasData {
- alias_def_id: d.alias_def_id,
- substs: d.substs,
- nested: d.nested.into_iter().map(f).collect(),
- }),
ImplSource::TraitUpcasting(d) => {
ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData {
- upcast_trait_ref: d.upcast_trait_ref,
vtable_vptr_slot: d.vtable_vptr_slot,
nested: d.nested.into_iter().map(f).collect(),
})
}
- ImplSource::ConstDestruct(i) => {
- ImplSource::ConstDestruct(ImplSourceConstDestructData {
- nested: i.nested.into_iter().map(f).collect(),
- })
- }
}
}
}
@@ -817,47 +735,7 @@ pub struct ImplSourceUserDefinedData<'tcx, N> {
#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceGeneratorData<'tcx, N> {
- pub generator_def_id: DefId,
- pub substs: SubstsRef<'tcx>,
- /// Nested obligations. This can be non-empty if the generator
- /// signature contains associated types.
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceFutureData<'tcx, N> {
- pub generator_def_id: DefId,
- pub substs: SubstsRef<'tcx>,
- /// Nested obligations. This can be non-empty if the generator
- /// signature contains associated types.
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceClosureData<'tcx, N> {
- pub closure_def_id: DefId,
- pub substs: SubstsRef<'tcx>,
- /// Nested obligations. This can be non-empty if the closure
- /// signature contains associated types.
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceAutoImplData<N> {
- pub trait_def_id: DefId,
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceTraitUpcastingData<'tcx, N> {
- /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
- pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
-
+pub struct ImplSourceTraitUpcastingData<N> {
/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the position of `upcast_trait_ref`'s vtable
@@ -867,18 +745,9 @@ pub struct ImplSourceTraitUpcastingData<'tcx, N> {
pub nested: Vec<N>,
}
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceBuiltinData<N> {
- pub nested: Vec<N>,
-}
-
#[derive(PartialEq, Eq, Clone, TyEncodable, TyDecodable, HashStable, Lift)]
#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceObjectData<'tcx, N> {
- /// `Foo` upcast to the obligation trait. This will be some supertrait of `Foo`.
- pub upcast_trait_ref: ty::PolyTraitRef<'tcx>,
-
+pub struct ImplSourceObjectData<N> {
/// The vtable is formed by concatenating together the method lists of
/// the base object trait and all supertraits, pointers to supertrait vtable will
/// be provided when necessary; this is the start of `upcast_trait_ref`'s methods
@@ -888,27 +757,6 @@ pub struct ImplSourceObjectData<'tcx, N> {
pub nested: Vec<N>,
}
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceFnPointerData<'tcx, N> {
- pub fn_ty: Ty<'tcx>,
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceConstDestructData<N> {
- pub nested: Vec<N>,
-}
-
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
-#[derive(TypeFoldable, TypeVisitable)]
-pub struct ImplSourceTraitAliasData<'tcx, N> {
- pub alias_def_id: DefId,
- pub substs: SubstsRef<'tcx>,
- pub nested: Vec<N>,
-}
-
#[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
pub enum ObjectSafetyViolation {
/// `Self: Sized` declared on the trait.
@@ -1109,7 +957,7 @@ pub enum CodegenObligationError {
FulfillmentError,
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum DefiningAnchor {
/// `DefId` of the item.
Bind(LocalDefId),
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index eae5a280e..60a38747f 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -92,7 +92,7 @@ pub type CanonicalTypeOpProvePredicateGoal<'tcx> =
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
-#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Hash, HashStable, PartialEq, Eq)]
pub struct NoSolution;
impl<'tcx> From<TypeError<'tcx>> for NoSolution {
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 2c5b64a59..73b332fd8 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -11,6 +11,8 @@ use crate::ty::{
TypeVisitor,
};
+pub mod inspect;
+
pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
/// A goal is a statement, i.e. `predicate`, we want to prove
@@ -18,7 +20,7 @@ pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
///
/// Most of the time the `param_env` contains the `where`-bounds of the function
/// we're currently typechecking while the `predicate` is some trait bound.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct Goal<'tcx, P> {
pub predicate: P,
pub param_env: ty::ParamEnv<'tcx>,
@@ -39,7 +41,7 @@ impl<'tcx, P> Goal<'tcx, P> {
}
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct Response<'tcx> {
pub certainty: Certainty,
pub var_values: CanonicalVarValues<'tcx>,
@@ -47,7 +49,7 @@ pub struct Response<'tcx> {
pub external_constraints: ExternalConstraints<'tcx>,
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum Certainty {
Yes,
Maybe(MaybeCause),
@@ -86,7 +88,7 @@ impl Certainty {
}
/// Why we failed to evaluate a goal.
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub enum MaybeCause {
/// We failed due to ambiguity. This ambiguity can either
/// be a true ambiguity, i.e. there are multiple different answers,
@@ -96,7 +98,7 @@ pub enum MaybeCause {
Overflow,
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
pub struct QueryInput<'tcx, T> {
pub goal: Goal<'tcx, T>,
pub anchor: DefiningAnchor,
@@ -104,12 +106,12 @@ pub struct QueryInput<'tcx, T> {
}
/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
pub struct PredefinedOpaquesData<'tcx> {
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
}
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
@@ -132,7 +134,7 @@ pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
/// solver, merge the two responses again.
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
-#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
@@ -144,7 +146,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
}
/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
pub struct ExternalConstraintsData<'tcx> {
// FIXME: implement this.
pub region_constraints: QueryRegionConstraints<'tcx>,
@@ -226,3 +228,9 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
self.opaque_types.visit_with(visitor)
}
}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
+pub enum IsNormalizesToHack {
+ Yes,
+ No,
+}
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
new file mode 100644
index 000000000..527afa005
--- /dev/null
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -0,0 +1,83 @@
+use super::{
+ CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, QueryResult,
+};
+use crate::ty;
+use format::ProofTreeFormatter;
+use std::fmt::{Debug, Write};
+
+mod format;
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub enum CacheHit {
+ Provisional,
+ Global,
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalEvaluation<'tcx> {
+ pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ pub canonicalized_goal: CanonicalInput<'tcx>,
+
+ pub kind: GoalEvaluationKind<'tcx>,
+ pub is_normalizes_to_hack: IsNormalizesToHack,
+ pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+
+ pub result: QueryResult<'tcx>,
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub enum GoalEvaluationKind<'tcx> {
+ CacheHit(CacheHit),
+ Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
+}
+impl Debug for GoalEvaluation<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ ProofTreeFormatter { f, on_newline: true }.format_goal_evaluation(self)
+ }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct AddedGoalsEvaluation<'tcx> {
+ pub evaluations: Vec<Vec<GoalEvaluation<'tcx>>>,
+ pub result: Result<Certainty, NoSolution>,
+}
+impl Debug for AddedGoalsEvaluation<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ ProofTreeFormatter { f, on_newline: true }.format_nested_goal_evaluation(self)
+ }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalEvaluationStep<'tcx> {
+ pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+ pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<GoalCandidate<'tcx>>,
+
+ pub result: QueryResult<'tcx>,
+}
+impl Debug for GoalEvaluationStep<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ ProofTreeFormatter { f, on_newline: true }.format_evaluation_step(self)
+ }
+}
+
+#[derive(Eq, PartialEq, Hash, HashStable)]
+pub struct GoalCandidate<'tcx> {
+ pub nested_goal_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<GoalCandidate<'tcx>>,
+ pub kind: CandidateKind<'tcx>,
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub enum CandidateKind<'tcx> {
+ /// Probe entered when normalizing the self ty during candidate assembly
+ NormalizedSelfTyAssembly,
+ /// A normal candidate for proving a goal
+ Candidate { name: String, result: QueryResult<'tcx> },
+}
+impl Debug for GoalCandidate<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ ProofTreeFormatter { f, on_newline: true }.format_candidate(self)
+ }
+}
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
new file mode 100644
index 000000000..2ee625674
--- /dev/null
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -0,0 +1,131 @@
+use super::*;
+
+pub(super) struct ProofTreeFormatter<'a, 'b> {
+ pub(super) f: &'a mut (dyn Write + 'b),
+ pub(super) on_newline: bool,
+}
+
+impl Write for ProofTreeFormatter<'_, '_> {
+ fn write_str(&mut self, s: &str) -> std::fmt::Result {
+ for line in s.split_inclusive("\n") {
+ if self.on_newline {
+ self.f.write_str(" ")?;
+ }
+ self.on_newline = line.ends_with("\n");
+ self.f.write_str(line)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl ProofTreeFormatter<'_, '_> {
+ fn nested(&mut self) -> ProofTreeFormatter<'_, '_> {
+ ProofTreeFormatter { f: self, on_newline: true }
+ }
+
+ pub(super) fn format_goal_evaluation(&mut self, goal: &GoalEvaluation<'_>) -> std::fmt::Result {
+ let f = &mut *self.f;
+
+ let goal_text = match goal.is_normalizes_to_hack {
+ IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
+ IsNormalizesToHack::No => "GOAL",
+ };
+
+ writeln!(f, "{}: {:?}", goal_text, goal.uncanonicalized_goal,)?;
+ writeln!(f, "CANONICALIZED: {:?}", goal.canonicalized_goal)?;
+
+ match &goal.kind {
+ GoalEvaluationKind::CacheHit(CacheHit::Global) => {
+ writeln!(f, "GLOBAL CACHE HIT: {:?}", goal.result)
+ }
+ GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
+ writeln!(f, "PROVISIONAL CACHE HIT: {:?}", goal.result)
+ }
+ GoalEvaluationKind::Uncached { revisions } => {
+ for (n, step) in revisions.iter().enumerate() {
+ let f = &mut *self.f;
+ writeln!(f, "REVISION {n}: {:?}", step.result)?;
+ let mut f = self.nested();
+ f.format_evaluation_step(step)?;
+ }
+
+ let f = &mut *self.f;
+ writeln!(f, "RESULT: {:?}", goal.result)
+ }
+ }?;
+
+ if goal.returned_goals.len() > 0 {
+ let f = &mut *self.f;
+ writeln!(f, "NESTED GOALS ADDED TO CALLER: [")?;
+ let mut f = self.nested();
+ for goal in goal.returned_goals.iter() {
+ writeln!(f, "ADDED GOAL: {:?},", goal)?;
+ }
+ writeln!(self.f, "]")?;
+ }
+
+ Ok(())
+ }
+
+ pub(super) fn format_evaluation_step(
+ &mut self,
+ evaluation_step: &GoalEvaluationStep<'_>,
+ ) -> std::fmt::Result {
+ let f = &mut *self.f;
+ writeln!(f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
+
+ for candidate in &evaluation_step.candidates {
+ let mut f = self.nested();
+ f.format_candidate(candidate)?;
+ }
+ for nested_goal_evaluation in &evaluation_step.nested_goal_evaluations {
+ let mut f = self.nested();
+ f.format_nested_goal_evaluation(nested_goal_evaluation)?;
+ }
+
+ Ok(())
+ }
+
+ pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
+ let f = &mut *self.f;
+
+ match &candidate.kind {
+ CandidateKind::NormalizedSelfTyAssembly => {
+ writeln!(f, "NORMALIZING SELF TY FOR ASSEMBLY:")
+ }
+ CandidateKind::Candidate { name, result } => {
+ writeln!(f, "CANDIDATE {}: {:?}", name, result)
+ }
+ }?;
+
+ let mut f = self.nested();
+ for candidate in &candidate.candidates {
+ f.format_candidate(candidate)?;
+ }
+ for nested_evaluations in &candidate.nested_goal_evaluations {
+ f.format_nested_goal_evaluation(nested_evaluations)?;
+ }
+
+ Ok(())
+ }
+
+ pub(super) fn format_nested_goal_evaluation(
+ &mut self,
+ nested_goal_evaluation: &AddedGoalsEvaluation<'_>,
+ ) -> std::fmt::Result {
+ let f = &mut *self.f;
+ writeln!(f, "TRY_EVALUATE_ADDED_GOALS: {:?}", nested_goal_evaluation.result)?;
+
+ for (n, revision) in nested_goal_evaluation.evaluations.iter().enumerate() {
+ let f = &mut *self.f;
+ writeln!(f, "REVISION {n}")?;
+ let mut f = self.nested();
+ for goal_evaluation in revision {
+ f.format_goal_evaluation(goal_evaluation)?;
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs
index c016f7227..dc2cd2035 100644
--- a/compiler/rustc_middle/src/traits/specialization_graph.rs
+++ b/compiler/rustc_middle/src/traits/specialization_graph.rs
@@ -228,7 +228,7 @@ impl<'tcx> Ancestors<'tcx> {
if let Some(item) = node.item(tcx, trait_item_def_id) {
if finalizing_node.is_none() {
let is_specializable = item.defaultness(tcx).is_default()
- || tcx.impl_defaultness(node.def_id()).is_default();
+ || tcx.defaultness(node.def_id()).is_default();
if !is_specializable {
finalizing_node = Some(node);
diff --git a/compiler/rustc_middle/src/traits/structural_impls.rs b/compiler/rustc_middle/src/traits/structural_impls.rs
index 6acb7745d..a703e3c95 100644
--- a/compiler/rustc_middle/src/traits/structural_impls.rs
+++ b/compiler/rustc_middle/src/traits/structural_impls.rs
@@ -9,15 +9,7 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
match *self {
super::ImplSource::UserDefined(ref v) => write!(f, "{:?}", v),
- super::ImplSource::AutoImpl(ref t) => write!(f, "{:?}", t),
-
- super::ImplSource::Closure(ref d) => write!(f, "{:?}", d),
-
- super::ImplSource::Generator(ref d) => write!(f, "{:?}", d),
-
- super::ImplSource::Future(ref d) => write!(f, "{:?}", d),
-
- super::ImplSource::FnPointer(ref d) => write!(f, "({:?})", d),
+ super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
super::ImplSource::Object(ref d) => write!(f, "{:?}", d),
@@ -25,13 +17,7 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSource<'tcx, N> {
write!(f, "ImplSourceParamData({:?}, {:?})", n, ct)
}
- super::ImplSource::Builtin(ref d) => write!(f, "{:?}", d),
-
- super::ImplSource::TraitAlias(ref d) => write!(f, "{:?}", d),
-
super::ImplSource::TraitUpcasting(ref d) => write!(f, "{:?}", d),
-
- super::ImplSource::ConstDestruct(ref d) => write!(f, "{:?}", d),
}
}
}
@@ -46,90 +32,22 @@ impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceUserDefinedData<'tcx,
}
}
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceGeneratorData<'tcx, N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "ImplSourceGeneratorData(generator_def_id={:?}, substs={:?}, nested={:?})",
- self.generator_def_id, self.substs, self.nested
- )
- }
-}
-
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFutureData<'tcx, N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "ImplSourceFutureData(generator_def_id={:?}, substs={:?}, nested={:?})",
- self.generator_def_id, self.substs, self.nested
- )
- }
-}
-
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceClosureData<'tcx, N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "ImplSourceClosureData(closure_def_id={:?}, substs={:?}, nested={:?})",
- self.closure_def_id, self.substs, self.nested
- )
- }
-}
-
-impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceBuiltinData<N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ImplSourceBuiltinData(nested={:?})", self.nested)
- }
-}
-
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<'tcx, N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "ImplSourceTraitUpcastingData(upcast={:?}, vtable_vptr_slot={:?}, nested={:?})",
- self.upcast_trait_ref, self.vtable_vptr_slot, self.nested
- )
- }
-}
-
-impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceAutoImplData<N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "ImplSourceAutoImplData(trait_def_id={:?}, nested={:?})",
- self.trait_def_id, self.nested
- )
- }
-}
-
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<'tcx, N> {
+impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitUpcastingData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
- "ImplSourceObjectData(upcast={:?}, vtable_base={}, nested={:?})",
- self.upcast_trait_ref, self.vtable_base, self.nested
+ "ImplSourceTraitUpcastingData(vtable_vptr_slot={:?}, nested={:?})",
+ self.vtable_vptr_slot, self.nested
)
}
}
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceFnPointerData<'tcx, N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ImplSourceFnPointerData(fn_ty={:?}, nested={:?})", self.fn_ty, self.nested)
- }
-}
-
-impl<'tcx, N: fmt::Debug> fmt::Debug for traits::ImplSourceTraitAliasData<'tcx, N> {
+impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceObjectData<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
- "ImplSourceTraitAliasData(alias_def_id={:?}, substs={:?}, nested={:?})",
- self.alias_def_id, self.substs, self.nested
+ "ImplSourceObjectData(vtable_base={}, nested={:?})",
+ self.vtable_base, self.nested
)
}
}
-
-impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "ImplSourceConstDestructData(nested={:?})", self.nested)
- }
-}
diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs
index d54b8c599..05c06efaf 100644
--- a/compiler/rustc_middle/src/traits/util.rs
+++ b/compiler/rustc_middle/src/traits/util.rs
@@ -25,9 +25,7 @@ impl<'tcx> Elaborator<'tcx> {
.super_predicates_of(trait_ref.def_id())
.predicates
.into_iter()
- .flat_map(|(pred, _)| {
- pred.subst_supertrait(self.tcx, &trait_ref).to_opt_poly_trait_pred()
- })
+ .flat_map(|(pred, _)| pred.subst_supertrait(self.tcx, &trait_ref).as_trait_clause())
.map(|t| t.map_bound(|pred| pred.trait_ref))
.filter(|supertrait_ref| self.visited.insert(*supertrait_ref));
diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs
index cbc68fde9..09517200b 100644
--- a/compiler/rustc_middle/src/ty/_match.rs
+++ b/compiler/rustc_middle/src/ty/_match.rs
@@ -81,7 +81,7 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> {
Err(TypeError::Sorts(relate::expected_found(self, a, b)))
}
- (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(self.tcx().ty_error(guar)),
+ (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)),
_ => relate::structurally_relate_tys(self, a, b),
}
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
index a39631da9..ffee7ba28 100644
--- a/compiler/rustc_middle/src/ty/abstract_const.rs
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -53,7 +53,7 @@ impl<'tcx> TyCtxt<'tcx> {
fn fold_const(&mut self, c: Const<'tcx>) -> Const<'tcx> {
let ct = match c.kind() {
ty::ConstKind::Unevaluated(uv) => match self.tcx.thir_abstract_const(uv.def) {
- Err(e) => self.tcx.const_error(c.ty(), e),
+ Err(e) => ty::Const::new_error(self.tcx, e, c.ty()),
Ok(Some(bac)) => {
let substs = self.tcx.erase_regions(uv.substs);
let bac = bac.subst(self.tcx, substs);
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index cd0f7e8da..76931ceaa 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -6,7 +6,7 @@ use rustc_span::Span;
use rustc_target::abi::FieldIdx;
#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
-pub enum PointerCast {
+pub enum PointerCoercion {
/// Go from a fn-item type to a fn-pointer type.
ReifyFnPointer,
@@ -99,7 +99,7 @@ pub enum Adjust<'tcx> {
/// Take the address and produce either a `&` or `*` pointer.
Borrow(AutoBorrow<'tcx>),
- Pointer(PointerCast),
+ Pointer(PointerCoercion),
/// Cast into a dyn* object.
DynStar,
@@ -132,7 +132,7 @@ impl<'tcx> OverloadedDeref<'tcx> {
.find(|m| m.kind == ty::AssocKind::Fn)
.unwrap()
.def_id;
- tcx.mk_fn_def(method_def_id, [source])
+ Ty::new_fn_def(tcx, method_def_id, [source])
}
}
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 7c5c030c2..e067d2a98 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -573,7 +573,7 @@ impl<'tcx> AdtDef<'tcx> {
/// Due to normalization being eager, this applies even if
/// the associated type is behind a pointer (e.g., issue #31299).
pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx [Ty<'tcx>]> {
- ty::EarlyBinder(tcx.adt_sized_constraint(self.did()))
+ ty::EarlyBinder::bind(tcx.adt_sized_constraint(self.did()))
}
}
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 090b76932..cce609c26 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -48,7 +48,7 @@ impl AssocItem {
///
/// [`type_of`]: crate::ty::TyCtxt::type_of
pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness {
- tcx.impl_defaultness(self.def_id)
+ tcx.defaultness(self.def_id)
}
#[inline]
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index be7b2b7ec..bc9273745 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -427,6 +427,8 @@ pub enum BorrowKind {
/// immutable, but not aliasable. This solves the problem. For
/// simplicity, we don't give users the way to express this
/// borrow, it's just used when translating closures.
+ ///
+ /// FIXME: Rename this to indicate the borrow is actually not immutable.
UniqueImmBorrow,
/// Data is mutable and not aliasable.
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 7fc75674d..6adbb44a1 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -128,6 +128,12 @@ impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Predicate<'tcx>
}
}
+impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Clause<'tcx> {
+ fn encode(&self, e: &mut E) {
+ self.as_predicate().encode(e);
+ }
+}
+
impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::Region<'tcx> {
fn encode(&self, e: &mut E) {
self.kind().encode(e);
@@ -241,6 +247,13 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Predicate<'tcx>
}
}
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Clause<'tcx> {
+ fn decode(decoder: &mut D) -> ty::Clause<'tcx> {
+ let pred: ty::Predicate<'tcx> = Decodable::decode(decoder);
+ pred.expect_clause()
+ }
+}
+
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for SubstsRef<'tcx> {
fn decode(decoder: &mut D) -> Self {
let len = decoder.read_usize();
@@ -264,7 +277,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for mir::Place<'tcx> {
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Region<'tcx> {
fn decode(decoder: &mut D) -> Self {
- decoder.interner().mk_region_from_kind(Decodable::decode(decoder))
+ ty::Region::new_from_kind(decoder.interner(), Decodable::decode(decoder))
}
}
@@ -331,7 +344,7 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Const<'tcx> {
fn decode(decoder: &mut D) -> Self {
let consts: ty::ConstData<'tcx> = Decodable::decode(decoder);
- decoder.interner().mk_const(consts.kind, consts.ty)
+ decoder.interner().mk_ct_from_kind(consts.kind, consts.ty)
}
}
@@ -355,16 +368,6 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for AdtDef<'tcx> {
}
}
-impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D>
- for [(ty::Predicate<'tcx>, Span)]
-{
- fn decode(decoder: &mut D) -> &'tcx Self {
- decoder.interner().arena.alloc_from_iter(
- (0..decoder.read_usize()).map(|_| Decodable::decode(decoder)).collect::<Vec<_>>(),
- )
- }
-}
-
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for [(ty::Clause<'tcx>, Span)] {
fn decode(decoder: &mut D) -> &'tcx Self {
decoder.interner().arena.alloc_from_iter(
@@ -393,11 +396,11 @@ impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty
}
}
-impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Predicate<'tcx>> {
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Clause<'tcx>> {
fn decode(decoder: &mut D) -> &'tcx Self {
let len = decoder.read_usize();
- decoder.interner().mk_predicates_from_iter(
- (0..len).map::<ty::Predicate<'tcx>, _>(|_| Decodable::decode(decoder)),
+ decoder.interner().mk_clauses_from_iter(
+ (0..len).map::<ty::Clause<'tcx>, _>(|_| Decodable::decode(decoder)),
)
}
}
@@ -421,7 +424,7 @@ impl_decodable_via_ref! {
&'tcx mir::BorrowCheckResult<'tcx>,
&'tcx mir::coverage::CodeRegion,
&'tcx ty::List<ty::BoundVariableKind>,
- &'tcx ty::List<ty::Predicate<'tcx>>,
+ &'tcx ty::List<ty::Clause<'tcx>>,
&'tcx ty::List<FieldIdx>,
}
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 1a4bd1481..1cbfe99f8 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -1,7 +1,8 @@
use crate::middle::resolve_bound_vars as rbv;
-use crate::mir::interpret::LitToConstInput;
-use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt};
+use crate::mir::interpret::{AllocId, ConstValue, LitToConstInput, Scalar};
+use crate::ty::{self, InternalSubsts, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
use rustc_data_structures::intern::Interned;
+use rustc_error_messages::MultiSpan;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::LocalDefId;
@@ -13,8 +14,13 @@ mod valtree;
pub use int::*;
pub use kind::*;
+use rustc_span::ErrorGuaranteed;
+use rustc_span::DUMMY_SP;
+use rustc_target::abi::Size;
pub use valtree::*;
+use super::sty::ConstKind;
+
/// Use this rather than `ConstData`, whenever possible.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
#[rustc_pass_by_value]
@@ -30,6 +36,16 @@ pub struct ConstData<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(ConstData<'_>, 40);
+enum EvalMode {
+ Typeck,
+ Mir,
+}
+
+enum EvalResult<'tcx> {
+ ValTree(ty::ValTree<'tcx>),
+ ConstVal(ConstValue<'tcx>),
+}
+
impl<'tcx> Const<'tcx> {
#[inline]
pub fn ty(self) -> Ty<'tcx> {
@@ -38,7 +54,98 @@ impl<'tcx> Const<'tcx> {
#[inline]
pub fn kind(self) -> ConstKind<'tcx> {
- self.0.kind
+ self.0.kind.clone()
+ }
+
+ #[inline]
+ pub fn new(tcx: TyCtxt<'tcx>, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ tcx.mk_ct_from_kind(kind, ty)
+ }
+
+ #[inline]
+ pub fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamConst, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Param(param), ty)
+ }
+
+ #[inline]
+ pub fn new_var(tcx: TyCtxt<'tcx>, infer: ty::ConstVid<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Var(infer)), ty)
+ }
+
+ #[inline]
+ pub fn new_fresh(tcx: TyCtxt<'tcx>, fresh: u32, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Infer(ty::InferConst::Fresh(fresh)), ty)
+ }
+
+ #[inline]
+ pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferConst<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Infer(infer), ty)
+ }
+
+ #[inline]
+ pub fn new_bound(
+ tcx: TyCtxt<'tcx>,
+ debruijn: ty::DebruijnIndex,
+ var: ty::BoundVar,
+ ty: Ty<'tcx>,
+ ) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Bound(debruijn, var), ty)
+ }
+
+ #[inline]
+ pub fn new_placeholder(
+ tcx: TyCtxt<'tcx>,
+ placeholder: ty::PlaceholderConst<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Placeholder(placeholder), ty)
+ }
+
+ #[inline]
+ pub fn new_unevaluated(
+ tcx: TyCtxt<'tcx>,
+ uv: ty::UnevaluatedConst<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Unevaluated(uv), ty)
+ }
+
+ #[inline]
+ pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Value(val), ty)
+ }
+
+ #[inline]
+ pub fn new_expr(tcx: TyCtxt<'tcx>, expr: ty::Expr<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Expr(expr), ty)
+ }
+
+ #[inline]
+ pub fn new_error(tcx: TyCtxt<'tcx>, e: ty::ErrorGuaranteed, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new(tcx, ty::ConstKind::Error(e), ty)
+ }
+
+ /// Like [Ty::new_error] but for constants.
+ #[track_caller]
+ pub fn new_misc_error(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ Const::new_error_with_message(
+ tcx,
+ ty,
+ DUMMY_SP,
+ "ty::ConstKind::Error constructed but no error reported",
+ )
+ }
+
+ /// Like [Ty::new_error_with_message] but for constants.
+ #[track_caller]
+ pub fn new_error_with_message<S: Into<MultiSpan>>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ span: S,
+ msg: &'static str,
+ ) -> Const<'tcx> {
+ let reported = tcx.sess.delay_span_bug(span, msg);
+ Const::new_error(tcx, reported, ty)
}
/// Literals and const generic parameters are eagerly converted to a constant, everything else
@@ -60,7 +167,8 @@ impl<'tcx> Const<'tcx> {
match Self::try_eval_lit_or_param(tcx, ty, expr) {
Some(v) => v,
- None => tcx.mk_const(
+ None => ty::Const::new_unevaluated(
+ tcx,
ty::UnevaluatedConst {
def: def.to_def_id(),
substs: InternalSubsts::identity_for_item(tcx, def.to_def_id()),
@@ -126,13 +234,19 @@ impl<'tcx> Const<'tcx> {
let generics = tcx.generics_of(item_def_id);
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.item_name(def_id);
- Some(tcx.mk_const(ty::ParamConst::new(index, name), param_ty))
+ Some(ty::Const::new_param(tcx, ty::ParamConst::new(index, name), param_ty))
+ }
+ Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => {
+ Some(ty::Const::new_bound(
+ tcx,
+ debruijn,
+ ty::BoundVar::from_u32(index),
+ param_ty,
+ ))
+ }
+ Some(rbv::ResolvedArg::Error(guar)) => {
+ Some(ty::Const::new_error(tcx, guar, param_ty))
}
- Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => Some(tcx.mk_const(
- ty::ConstKind::Bound(debruijn, ty::BoundVar::from_u32(index)),
- param_ty,
- )),
- Some(rbv::ResolvedArg::Error(guar)) => Some(tcx.const_error(param_ty, guar)),
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", expr.hir_id),
}
}
@@ -155,7 +269,8 @@ impl<'tcx> Const<'tcx> {
.layout_of(ty)
.unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
.size;
- tcx.mk_const(
+ ty::Const::new_value(
+ tcx,
ty::ValTree::from_scalar_int(ScalarInt::try_from_uint(bits, size).unwrap()),
ty.value,
)
@@ -164,7 +279,7 @@ impl<'tcx> Const<'tcx> {
#[inline]
/// Creates an interned zst constant.
pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
- tcx.mk_const(ty::ValTree::zst(), ty)
+ ty::Const::new_value(tcx, ty::ValTree::zst(), ty)
}
#[inline]
@@ -192,12 +307,12 @@ impl<'tcx> Const<'tcx> {
assert_eq!(self.ty(), ty);
let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
- self.kind().eval(tcx, param_env).try_to_bits(size)
+ self.eval(tcx, param_env).try_to_bits(size)
}
#[inline]
pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
- self.kind().eval(tcx, param_env).try_to_bool()
+ self.eval(tcx, param_env).try_to_bool()
}
#[inline]
@@ -206,17 +321,17 @@ impl<'tcx> Const<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
) -> Option<u64> {
- self.kind().eval(tcx, param_env).try_to_target_usize(tcx)
+ self.eval(tcx, param_env).try_to_target_usize(tcx)
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
/// unevaluated constant.
pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
- if let Some(val) = self.kind().try_eval_for_typeck(tcx, param_env) {
+ if let Some(val) = self.try_eval_for_typeck(tcx, param_env) {
match val {
- Ok(val) => tcx.mk_const(val, self.ty()),
- Err(guar) => tcx.const_error(self.ty(), guar),
+ Ok(val) => ty::Const::new_value(tcx, val, self.ty()),
+ Err(guar) => ty::Const::new_error(tcx, guar, self.ty()),
}
} else {
// Either the constant isn't evaluatable or ValTree creation failed.
@@ -238,6 +353,138 @@ impl<'tcx> Const<'tcx> {
.unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
}
+ #[inline]
+ /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
+ /// return `None`.
+ // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
+ pub fn try_eval_for_mir(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
+ match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
+ Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
+ Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
+ Some(Err(e)) => Some(Err(e)),
+ None => None,
+ }
+ }
+
+ #[inline]
+ /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
+ /// return `None`.
+ // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
+ pub fn try_eval_for_typeck(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
+ match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
+ Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
+ Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
+ Some(Err(e)) => Some(Err(e)),
+ None => None,
+ }
+ }
+
+ #[inline]
+ fn try_eval_inner(
+ self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ eval_mode: EvalMode,
+ ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
+ assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
+ if let ConstKind::Unevaluated(unevaluated) = self.kind() {
+ use crate::mir::interpret::ErrorHandled;
+
+ // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
+ // also does later, but we want to do it before checking for
+ // inference variables.
+ // Note that we erase regions *before* calling `with_reveal_all_normalized`,
+ // so that we don't try to invoke this query with
+ // any region variables.
+
+ // HACK(eddyb) when the query key would contain inference variables,
+ // attempt using identity substs and `ParamEnv` instead, that will succeed
+ // when the expression doesn't depend on any parameters.
+ // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
+ // we can call `infcx.const_eval_resolve` which handles inference variables.
+ let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
+ tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
+ def: unevaluated.def,
+ substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
+ })
+ } else {
+ tcx.erase_regions(param_env)
+ .with_reveal_all_normalized(tcx)
+ .and(tcx.erase_regions(unevaluated))
+ };
+
+ // FIXME(eddyb) maybe the `const_eval_*` methods should take
+ // `ty::ParamEnvAnd` instead of having them separate.
+ let (param_env, unevaluated) = param_env_and.into_parts();
+ // try to resolve e.g. associated constants to their definition on an impl, and then
+ // evaluate the const.
+ match eval_mode {
+ EvalMode::Typeck => {
+ match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
+ // NOTE(eddyb) `val` contains no lifetimes/types/consts,
+ // and we use the original type, so nothing from `substs`
+ // (which may be identity substs, see above),
+ // can leak through `val` into the const we return.
+ Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
+ Err(ErrorHandled::TooGeneric) => None,
+ Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
+ }
+ }
+ EvalMode::Mir => {
+ match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
+ // NOTE(eddyb) `val` contains no lifetimes/types/consts,
+ // and we use the original type, so nothing from `substs`
+ // (which may be identity substs, see above),
+ // can leak through `val` into the const we return.
+ Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
+ Err(ErrorHandled::TooGeneric) => None,
+ Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
+ }
+ }
+ }
+ } else {
+ None
+ }
+ }
+
+ #[inline]
+ pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
+ if let ConstKind::Value(val) = self.kind() { Some(val) } else { None }
+ }
+
+ #[inline]
+ pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
+ self.try_to_value()?.try_to_scalar()
+ }
+
+ #[inline]
+ pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
+ self.try_to_value()?.try_to_scalar_int()
+ }
+
+ #[inline]
+ pub fn try_to_bits(self, size: Size) -> Option<u128> {
+ self.try_to_scalar_int()?.to_bits(size).ok()
+ }
+
+ #[inline]
+ pub fn try_to_bool(self) -> Option<bool> {
+ self.try_to_scalar_int()?.try_into().ok()
+ }
+
+ #[inline]
+ pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
+ self.try_to_value()?.try_to_target_usize(tcx)
+ }
+
pub fn is_ct_infer(self) -> bool {
matches!(self.kind(), ty::ConstKind::Infer(_))
}
@@ -254,5 +501,5 @@ pub fn const_param_default(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBind
"`const_param_default` expected a generic parameter with a constant"
),
};
- ty::EarlyBinder(Const::from_anon_const(tcx, default_def_id))
+ ty::EarlyBinder::bind(Const::from_anon_const(tcx, default_def_id))
}
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index d1dbc531e..1e43fab45 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -1,5 +1,6 @@
use rustc_apfloat::ieee::{Double, Single};
use rustc_apfloat::Float;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_target::abi::Size;
use std::fmt;
@@ -113,6 +114,14 @@ impl std::fmt::Debug for ConstInt {
}
}
+impl IntoDiagnosticArg for ConstInt {
+ // FIXME this simply uses the Debug impl, but we could probably do better by converting both
+ // to an inherent method that returns `Cow`.
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ DiagnosticArgValue::Str(format!("{self:?}").into())
+ }
+}
+
/// The raw bytes of a simple value.
///
/// This is a packed struct in order to allow this type to be optimally embedded in enums
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index 1dd4f8a24..a6bf74911 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -1,17 +1,11 @@
use super::Const;
use crate::mir;
-use crate::mir::interpret::{AllocId, ConstValue, Scalar};
use crate::ty::abstract_const::CastKind;
-use crate::ty::subst::{InternalSubsts, SubstsRef};
-use crate::ty::ParamEnv;
-use crate::ty::{self, List, Ty, TyCtxt, TypeVisitableExt};
+use crate::ty::subst::SubstsRef;
+use crate::ty::{self, List, Ty};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
-use rustc_target::abi::Size;
-
-use super::ScalarInt;
/// An unevaluated (potentially generic) constant used in the type-system.
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
@@ -41,45 +35,6 @@ impl<'tcx> UnevaluatedConst<'tcx> {
}
}
-/// Represents a constant in Rust.
-#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
-#[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
-#[derive(derive_more::From)]
-pub enum ConstKind<'tcx> {
- /// A const generic parameter.
- Param(ty::ParamConst),
-
- /// Infer the value of the const.
- Infer(InferConst<'tcx>),
-
- /// Bound const variable, used only when preparing a trait query.
- Bound(ty::DebruijnIndex, ty::BoundVar),
-
- /// A placeholder const - universally quantified higher-ranked const.
- Placeholder(ty::PlaceholderConst<'tcx>),
-
- /// Used in the HIR by using `Unevaluated` everywhere and later normalizing to one of the other
- /// variants when the code is monomorphic enough for that.
- Unevaluated(UnevaluatedConst<'tcx>),
-
- /// Used to hold computed value.
- Value(ty::ValTree<'tcx>),
-
- /// A placeholder for a const which could not be computed; this is
- /// propagated to avoid useless error messages.
- #[from(ignore)]
- Error(ErrorGuaranteed),
-
- /// Expr which contains an expression which has partially evaluated items.
- Expr(Expr<'tcx>),
-}
-
-impl<'tcx> From<ty::ConstVid<'tcx>> for ConstKind<'tcx> {
- fn from(const_vid: ty::ConstVid<'tcx>) -> Self {
- InferConst::Var(const_vid).into()
- }
-}
-
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeVisitable, TypeFoldable)]
pub enum Expr<'tcx> {
@@ -93,39 +48,7 @@ pub enum Expr<'tcx> {
static_assert_size!(Expr<'_>, 24);
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstKind<'_>, 32);
-
-impl<'tcx> ConstKind<'tcx> {
- #[inline]
- pub fn try_to_value(self) -> Option<ty::ValTree<'tcx>> {
- if let ConstKind::Value(val) = self { Some(val) } else { None }
- }
-
- #[inline]
- pub fn try_to_scalar(self) -> Option<Scalar<AllocId>> {
- self.try_to_value()?.try_to_scalar()
- }
-
- #[inline]
- pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
- self.try_to_value()?.try_to_scalar_int()
- }
-
- #[inline]
- pub fn try_to_bits(self, size: Size) -> Option<u128> {
- self.try_to_scalar_int()?.to_bits(size).ok()
- }
-
- #[inline]
- pub fn try_to_bool(self) -> Option<bool> {
- self.try_to_scalar_int()?.try_into().ok()
- }
-
- #[inline]
- pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
- self.try_to_value()?.try_to_target_usize(tcx)
- }
-}
+static_assert_size!(super::ConstKind<'_>, 32);
/// An inference variable for a const, for use in const generics.
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Hash)]
@@ -144,124 +67,3 @@ impl<CTX> HashStable<CTX> for InferConst<'_> {
}
}
}
-
-enum EvalMode {
- Typeck,
- Mir,
-}
-
-enum EvalResult<'tcx> {
- ValTree(ty::ValTree<'tcx>),
- ConstVal(ConstValue<'tcx>),
-}
-
-impl<'tcx> ConstKind<'tcx> {
- #[inline]
- /// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
- /// unevaluated constant.
- pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
- self.try_eval_for_typeck(tcx, param_env).and_then(Result::ok).map_or(self, ConstKind::Value)
- }
-
- #[inline]
- /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
- /// return `None`.
- // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
- pub fn try_eval_for_mir(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- ) -> Option<Result<ConstValue<'tcx>, ErrorGuaranteed>> {
- match self.try_eval_inner(tcx, param_env, EvalMode::Mir) {
- Some(Ok(EvalResult::ValTree(_))) => unreachable!(),
- Some(Ok(EvalResult::ConstVal(v))) => Some(Ok(v)),
- Some(Err(e)) => Some(Err(e)),
- None => None,
- }
- }
-
- #[inline]
- /// Tries to evaluate the constant if it is `Unevaluated`. If that isn't possible or necessary
- /// return `None`.
- // FIXME(@lcnr): Completely rework the evaluation/normalization system for `ty::Const` once valtrees are merged.
- pub fn try_eval_for_typeck(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- ) -> Option<Result<ty::ValTree<'tcx>, ErrorGuaranteed>> {
- match self.try_eval_inner(tcx, param_env, EvalMode::Typeck) {
- Some(Ok(EvalResult::ValTree(v))) => Some(Ok(v)),
- Some(Ok(EvalResult::ConstVal(_))) => unreachable!(),
- Some(Err(e)) => Some(Err(e)),
- None => None,
- }
- }
-
- #[inline]
- fn try_eval_inner(
- self,
- tcx: TyCtxt<'tcx>,
- param_env: ParamEnv<'tcx>,
- eval_mode: EvalMode,
- ) -> Option<Result<EvalResult<'tcx>, ErrorGuaranteed>> {
- assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
- if let ConstKind::Unevaluated(unevaluated) = self {
- use crate::mir::interpret::ErrorHandled;
-
- // HACK(eddyb) this erases lifetimes even though `const_eval_resolve`
- // also does later, but we want to do it before checking for
- // inference variables.
- // Note that we erase regions *before* calling `with_reveal_all_normalized`,
- // so that we don't try to invoke this query with
- // any region variables.
-
- // HACK(eddyb) when the query key would contain inference variables,
- // attempt using identity substs and `ParamEnv` instead, that will succeed
- // when the expression doesn't depend on any parameters.
- // FIXME(eddyb, skinny121) pass `InferCtxt` into here when it's available, so that
- // we can call `infcx.const_eval_resolve` which handles inference variables.
- let param_env_and = if (param_env, unevaluated).has_non_region_infer() {
- tcx.param_env(unevaluated.def).and(ty::UnevaluatedConst {
- def: unevaluated.def,
- substs: InternalSubsts::identity_for_item(tcx, unevaluated.def),
- })
- } else {
- tcx.erase_regions(param_env)
- .with_reveal_all_normalized(tcx)
- .and(tcx.erase_regions(unevaluated))
- };
-
- // FIXME(eddyb) maybe the `const_eval_*` methods should take
- // `ty::ParamEnvAnd` instead of having them separate.
- let (param_env, unevaluated) = param_env_and.into_parts();
- // try to resolve e.g. associated constants to their definition on an impl, and then
- // evaluate the const.
- match eval_mode {
- EvalMode::Typeck => {
- match tcx.const_eval_resolve_for_typeck(param_env, unevaluated, None) {
- // NOTE(eddyb) `val` contains no lifetimes/types/consts,
- // and we use the original type, so nothing from `substs`
- // (which may be identity substs, see above),
- // can leak through `val` into the const we return.
- Ok(val) => Some(Ok(EvalResult::ValTree(val?))),
- Err(ErrorHandled::TooGeneric) => None,
- Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
- }
- }
- EvalMode::Mir => {
- match tcx.const_eval_resolve(param_env, unevaluated.expand(), None) {
- // NOTE(eddyb) `val` contains no lifetimes/types/consts,
- // and we use the original type, so nothing from `substs`
- // (which may be identity substs, see above),
- // can leak through `val` into the const we return.
- Ok(val) => Some(Ok(EvalResult::ConstVal(val))),
- Err(ErrorHandled::TooGeneric) => None,
- Err(ErrorHandled::Reported(e)) => Some(Err(e.into())),
- }
- }
- }
- } else {
- None
- }
- }
-}
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 2bde55bc4..035e978f6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -25,10 +25,10 @@ use crate::traits::solve::{
ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData,
};
use crate::ty::{
- self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid,
- GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
- PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind, ReprOptions,
- TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, UintTy, Visibility,
+ self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
+ ImplPolarity, InferTy, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig,
+ Predicate, PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind,
+ TyVid, TypeAndMut, Visibility,
};
use crate::ty::{GenericArg, InternalSubsts, SubstsRef};
use rustc_ast::{self as ast, attr};
@@ -70,10 +70,9 @@ use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}
use rustc_target::spec::abi;
use rustc_type_ir::sty::TyKind::*;
use rustc_type_ir::WithCachedTypeInfo;
-use rustc_type_ir::{CollectAndApply, DynKind, Interner, TypeFlags};
+use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
use std::any::Any;
-use std::assert_matches::debug_assert_matches;
use std::borrow::Borrow;
use std::cmp::Ordering;
use std::fmt;
@@ -82,8 +81,6 @@ use std::iter;
use std::mem;
use std::ops::{Bound, Deref};
-const TINY_CONST_EVAL_LIMIT: Limit = Limit(20);
-
#[allow(rustc::usage_of_ty_tykind)]
impl<'tcx> Interner for TyCtxt<'tcx> {
type AdtDef = ty::AdtDef<'tcx>;
@@ -110,11 +107,29 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type PredicateKind = ty::PredicateKind<'tcx>;
type AllocId = crate::mir::interpret::AllocId;
+ type InferConst = ty::InferConst<'tcx>;
+ type AliasConst = ty::UnevaluatedConst<'tcx>;
+ type ParamConst = ty::ParamConst;
+ type BoundConst = ty::BoundVar;
+ type PlaceholderConst = ty::PlaceholderConst<'tcx>;
+ type ValueConst = ty::ValTree<'tcx>;
+ type ExprConst = ty::Expr<'tcx>;
+
type EarlyBoundRegion = ty::EarlyBoundRegion;
type BoundRegion = ty::BoundRegion;
type FreeRegion = ty::FreeRegion;
type RegionVid = ty::RegionVid;
type PlaceholderRegion = ty::PlaceholderRegion;
+
+ fn ty_and_mut_to_parts(
+ TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>,
+ ) -> (Self::Ty, Self::Mutability) {
+ (ty, mutbl)
+ }
+
+ fn mutability_is_mut(mutbl: Self::Mutability) -> bool {
+ mutbl.is_mut()
+ }
}
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
@@ -133,7 +148,7 @@ pub struct CtxtInterners<'tcx> {
region: InternedSet<'tcx, RegionKind<'tcx>>,
poly_existential_predicates: InternedSet<'tcx, List<PolyExistentialPredicate<'tcx>>>,
predicate: InternedSet<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
- predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
+ clauses: InternedSet<'tcx, List<Clause<'tcx>>>,
projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
const_: InternedSet<'tcx, ConstData<'tcx>>,
@@ -158,7 +173,7 @@ impl<'tcx> CtxtInterners<'tcx> {
poly_existential_predicates: Default::default(),
canonical_var_infos: Default::default(),
predicate: Default::default(),
- predicates: Default::default(),
+ clauses: Default::default(),
projs: Default::default(),
place_elems: Default::default(),
const_: Default::default(),
@@ -312,6 +327,8 @@ pub struct CommonLifetimes<'tcx> {
pub struct CommonConsts<'tcx> {
pub unit: Const<'tcx>,
+ pub true_: Const<'tcx>,
+ pub false_: Const<'tcx>,
}
impl<'tcx> CommonTypes<'tcx> {
@@ -409,6 +426,14 @@ impl<'tcx> CommonConsts<'tcx> {
kind: ty::ConstKind::Value(ty::ValTree::zst()),
ty: types.unit,
}),
+ true_: mk_const(ty::ConstData {
+ kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::TRUE)),
+ ty: types.bool,
+ }),
+ false_: mk_const(ty::ConstData {
+ kind: ty::ConstKind::Value(ty::ValTree::Leaf(ty::ScalarInt::FALSE)),
+ ty: types.bool,
+ }),
}
}
}
@@ -470,6 +495,17 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
/// [rustc dev guide] for more details.
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ty.html
+///
+/// An implementation detail: `TyCtxt` is a wrapper type for [GlobalCtxt],
+/// which is the struct that actually holds all the data. `TyCtxt` derefs to
+/// `GlobalCtxt`, and in practice `TyCtxt` is passed around everywhere, and all
+/// operations are done via `TyCtxt`. A `TyCtxt` is obtained for a `GlobalCtxt`
+/// by calling `enter` with a closure `f`. That function creates both the
+/// `TyCtxt`, and an `ImplicitCtxt` around it that is put into TLS. Within `f`:
+/// - The `ImplicitCtxt` is available implicitly via TLS.
+/// - The `TyCtxt` is available explicitly via the `tcx` parameter, and also
+/// implicitly within the `ImplicitCtxt`. Explicit access is preferred when
+/// possible.
#[derive(Copy, Clone)]
#[rustc_diagnostic_item = "TyCtxt"]
#[rustc_pass_by_value]
@@ -485,6 +521,7 @@ impl<'tcx> Deref for TyCtxt<'tcx> {
}
}
+/// See [TyCtxt] for details about this type.
pub struct GlobalCtxt<'tcx> {
pub arena: &'tcx WorkerLocal<Arena<'tcx>>,
pub hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>,
@@ -689,82 +726,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
- /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
- #[track_caller]
- pub fn ty_error(self, reported: ErrorGuaranteed) -> Ty<'tcx> {
- self.mk_ty_from_kind(Error(reported))
- }
-
- /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
- #[track_caller]
- pub fn ty_error_misc(self) -> Ty<'tcx> {
- self.ty_error_with_message(DUMMY_SP, "TyKind::Error constructed but no error reported")
- }
-
- /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
- /// ensure it gets used.
- #[track_caller]
- pub fn ty_error_with_message<S: Into<MultiSpan>>(
- self,
- span: S,
- msg: impl Into<DiagnosticMessage>,
- ) -> Ty<'tcx> {
- let reported = self.sess.delay_span_bug(span, msg);
- self.mk_ty_from_kind(Error(reported))
- }
-
- /// Constructs a `RegionKind::ReError` lifetime.
- #[track_caller]
- pub fn mk_re_error(self, reported: ErrorGuaranteed) -> Region<'tcx> {
- self.intern_region(ty::ReError(reported))
- }
-
- /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` to ensure it
- /// gets used.
- #[track_caller]
- pub fn mk_re_error_misc(self) -> Region<'tcx> {
- self.mk_re_error_with_message(
- DUMMY_SP,
- "RegionKind::ReError constructed but no error reported",
- )
- }
-
- /// Constructs a `RegionKind::ReError` lifetime and registers a `delay_span_bug` with the given
- /// `msg` to ensure it gets used.
- #[track_caller]
- pub fn mk_re_error_with_message<S: Into<MultiSpan>>(self, span: S, msg: &str) -> Region<'tcx> {
- let reported = self.sess.delay_span_bug(span, msg);
- self.mk_re_error(reported)
- }
-
- /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed`
- #[track_caller]
- pub fn const_error(self, ty: Ty<'tcx>, reported: ErrorGuaranteed) -> Const<'tcx> {
- self.mk_const(ty::ConstKind::Error(reported), ty)
- }
-
- /// Like [TyCtxt::ty_error] but for constants.
- #[track_caller]
- pub fn const_error_misc(self, ty: Ty<'tcx>) -> Const<'tcx> {
- self.const_error_with_message(
- ty,
- DUMMY_SP,
- "ty::ConstKind::Error constructed but no error reported",
- )
- }
-
- /// Like [TyCtxt::ty_error_with_message] but for constants.
- #[track_caller]
- pub fn const_error_with_message<S: Into<MultiSpan>>(
- self,
- ty: Ty<'tcx>,
- span: S,
- msg: &str,
- ) -> Const<'tcx> {
- let reported = self.sess.delay_span_bug(span, msg);
- self.mk_const(ty::ConstKind::Error(reported), ty)
- }
-
pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg)
}
@@ -1019,15 +980,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.query_system.on_disk_cache.as_ref().map_or(Ok(0), |c| c.serialize(self, encoder))
}
- /// If `true`, we should use lazy normalization for constants, otherwise
- /// we still evaluate them eagerly.
- #[inline]
- pub fn lazy_normalization(self) -> bool {
- let features = self.features();
- // Note: We only use lazy normalization for generic const expressions.
- features.generic_const_exprs
- }
-
#[inline]
pub fn local_crate_exports_generics(self) -> bool {
debug_assert!(self.sess.opts.share_generics());
@@ -1167,7 +1119,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// Returns `&'static core::panic::Location<'static>`.
pub fn caller_location_ty(self) -> Ty<'tcx> {
- self.mk_imm_ref(
+ Ty::new_imm_ref(
+ self,
self.lifetimes.re_static,
self.type_of(self.require_lang_item(LangItem::PanicLocation, None))
.subst(self, self.mk_substs(&[self.lifetimes.re_static.into()])),
@@ -1192,14 +1145,6 @@ impl<'tcx> TyCtxt<'tcx> {
self.limits(()).move_size_limit
}
- pub fn const_eval_limit(self) -> Limit {
- if self.sess.opts.unstable_opts.tiny_const_eval_limit {
- TINY_CONST_EVAL_LIMIT
- } else {
- self.limits(()).const_eval_limit
- }
- }
-
pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
iter::once(LOCAL_CRATE)
.chain(self.crates(()).iter().copied())
@@ -1210,6 +1155,12 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn local_visibility(self, def_id: LocalDefId) -> Visibility {
self.visibility(def_id).expect_local()
}
+
+ /// Returns the origin of the opaque type `def_id`.
+ #[instrument(skip(self), level = "trace", ret)]
+ pub fn opaque_type_origin(self, def_id: LocalDefId) -> hir::OpaqueTyOrigin {
+ self.hir().expect_item(def_id).expect_opaque_ty().origin
+ }
}
/// A trait implemented for all `X<'a>` types that can be safely and
@@ -1272,10 +1223,11 @@ nop_lift! {region; Region<'a> => Region<'tcx>}
nop_lift! {const_; Const<'a> => Const<'tcx>}
nop_lift! {const_allocation; ConstAllocation<'a> => ConstAllocation<'tcx>}
nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
+nop_lift! {predicate; Clause<'a> => Clause<'tcx>}
nop_list_lift! {type_lists; Ty<'a> => Ty<'tcx>}
nop_list_lift! {poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>}
-nop_list_lift! {predicates; Predicate<'a> => Predicate<'tcx>}
+nop_list_lift! {clauses; Clause<'a> => Clause<'tcx>}
nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
nop_list_lift! {projs; ProjectionKind => ProjectionKind}
nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind}
@@ -1515,9 +1467,9 @@ macro_rules! direct_interners {
// Functions with a `mk_` prefix are intended for use outside this file and
// crate. Functions with an `intern_` prefix are intended for use within this
-// file only, and have a corresponding `mk_` function.
+// crate only, and have a corresponding `mk_` function.
direct_interners! {
- region: intern_region(RegionKind<'tcx>): Region -> Region<'tcx>,
+ region: pub(crate) intern_region(RegionKind<'tcx>): Region -> Region<'tcx>,
const_: intern_const(ConstData<'tcx>): Const -> Const<'tcx>,
const_allocation: pub mk_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
layout: pub mk_layout(LayoutS): Layout -> Layout<'tcx>,
@@ -1553,7 +1505,7 @@ slice_interners!(
type_lists: pub mk_type_list(Ty<'tcx>),
canonical_var_infos: pub mk_canonical_var_infos(CanonicalVarInfo<'tcx>),
poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
- predicates: intern_predicates(Predicate<'tcx>),
+ clauses: intern_clauses(Clause<'tcx>),
projs: pub mk_projs(ProjectionKind),
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
@@ -1566,7 +1518,10 @@ impl<'tcx> TyCtxt<'tcx> {
/// unsafe.
pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
assert_eq!(sig.unsafety(), hir::Unsafety::Normal);
- self.mk_fn_ptr(sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }))
+ Ty::new_fn_ptr(
+ self,
+ sig.map_bound(|sig| ty::FnSig { unsafety: hir::Unsafety::Unsafe, ..sig }),
+ )
}
/// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
@@ -1585,7 +1540,7 @@ impl<'tcx> TyCtxt<'tcx> {
let future_trait = self.require_lang_item(LangItem::Future, None);
self.explicit_item_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| {
- let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() else {
+ let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else {
return false;
};
trait_predicate.trait_ref.def_id == future_trait
@@ -1608,9 +1563,7 @@ impl<'tcx> TyCtxt<'tcx> {
let generic_predicates = self.super_predicates_of(trait_did);
for (predicate, _) in generic_predicates.predicates {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(data)) =
- predicate.kind().skip_binder()
- {
+ if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() {
if set.insert(data.def_id()) {
stack.push(data.def_id());
}
@@ -1642,18 +1595,6 @@ impl<'tcx> TyCtxt<'tcx> {
})
}
- // Avoid this in favour of more specific `mk_*` methods, where possible.
- #[allow(rustc::usage_of_ty_tykind)]
- #[inline]
- pub fn mk_ty_from_kind(self, st: TyKind<'tcx>) -> Ty<'tcx> {
- self.interners.intern_ty(
- st,
- self.sess,
- // This is only used to create a stable hashing context.
- &self.untracked,
- )
- }
-
#[inline]
pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
self.interners.intern_predicate(
@@ -1673,174 +1614,6 @@ impl<'tcx> TyCtxt<'tcx> {
if pred.kind() != binder { self.mk_predicate(binder) } else { pred }
}
- pub fn mk_mach_int(self, tm: IntTy) -> Ty<'tcx> {
- match tm {
- IntTy::Isize => self.types.isize,
- IntTy::I8 => self.types.i8,
- IntTy::I16 => self.types.i16,
- IntTy::I32 => self.types.i32,
- IntTy::I64 => self.types.i64,
- IntTy::I128 => self.types.i128,
- }
- }
-
- pub fn mk_mach_uint(self, tm: UintTy) -> Ty<'tcx> {
- match tm {
- UintTy::Usize => self.types.usize,
- UintTy::U8 => self.types.u8,
- UintTy::U16 => self.types.u16,
- UintTy::U32 => self.types.u32,
- UintTy::U64 => self.types.u64,
- UintTy::U128 => self.types.u128,
- }
- }
-
- pub fn mk_mach_float(self, tm: FloatTy) -> Ty<'tcx> {
- match tm {
- FloatTy::F32 => self.types.f32,
- FloatTy::F64 => self.types.f64,
- }
- }
-
- #[inline]
- pub fn mk_static_str(self) -> Ty<'tcx> {
- self.mk_imm_ref(self.lifetimes.re_static, self.types.str_)
- }
-
- #[inline]
- pub fn mk_adt(self, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
- // Take a copy of substs so that we own the vectors inside.
- self.mk_ty_from_kind(Adt(def, substs))
- }
-
- #[inline]
- pub fn mk_foreign(self, def_id: DefId) -> Ty<'tcx> {
- self.mk_ty_from_kind(Foreign(def_id))
- }
-
- fn mk_generic_adt(self, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
- let adt_def = self.adt_def(wrapper_def_id);
- let substs =
- InternalSubsts::for_item(self, wrapper_def_id, |param, substs| match param.kind {
- GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => bug!(),
- GenericParamDefKind::Type { has_default, .. } => {
- if param.index == 0 {
- ty_param.into()
- } else {
- assert!(has_default);
- self.type_of(param.def_id).subst(self, substs).into()
- }
- }
- });
- self.mk_ty_from_kind(Adt(adt_def, substs))
- }
-
- #[inline]
- pub fn mk_box(self, ty: Ty<'tcx>) -> Ty<'tcx> {
- let def_id = self.require_lang_item(LangItem::OwnedBox, None);
- self.mk_generic_adt(def_id, ty)
- }
-
- #[inline]
- pub fn mk_lang_item(self, ty: Ty<'tcx>, item: LangItem) -> Option<Ty<'tcx>> {
- let def_id = self.lang_items().get(item)?;
- Some(self.mk_generic_adt(def_id, ty))
- }
-
- #[inline]
- pub fn mk_diagnostic_item(self, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
- let def_id = self.get_diagnostic_item(name)?;
- Some(self.mk_generic_adt(def_id, ty))
- }
-
- #[inline]
- pub fn mk_maybe_uninit(self, ty: Ty<'tcx>) -> Ty<'tcx> {
- let def_id = self.require_lang_item(LangItem::MaybeUninit, None);
- self.mk_generic_adt(def_id, ty)
- }
-
- #[inline]
- pub fn mk_ptr(self, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(RawPtr(tm))
- }
-
- #[inline]
- pub fn mk_ref(self, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(Ref(r, tm.ty, tm.mutbl))
- }
-
- #[inline]
- pub fn mk_mut_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
- }
-
- #[inline]
- pub fn mk_imm_ref(self, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.mk_ref(r, TypeAndMut { ty, mutbl: hir::Mutability::Not })
- }
-
- #[inline]
- pub fn mk_mut_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Mut })
- }
-
- #[inline]
- pub fn mk_imm_ptr(self, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.mk_ptr(TypeAndMut { ty, mutbl: hir::Mutability::Not })
- }
-
- #[inline]
- pub fn mk_array(self, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
- self.mk_ty_from_kind(Array(ty, ty::Const::from_target_usize(self, n)))
- }
-
- #[inline]
- pub fn mk_array_with_const_len(self, ty: Ty<'tcx>, ct: Const<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(Array(ty, ct))
- }
-
- #[inline]
- pub fn mk_slice(self, ty: Ty<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(Slice(ty))
- }
-
- #[inline]
- pub fn mk_tup(self, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
- if ts.is_empty() {
- self.types.unit
- } else {
- self.mk_ty_from_kind(Tuple(self.mk_type_list(&ts)))
- }
- }
-
- pub fn mk_tup_from_iter<I, T>(self, iter: I) -> T::Output
- where
- I: Iterator<Item = T>,
- T: CollectAndApply<Ty<'tcx>, Ty<'tcx>>,
- {
- T::collect_and_apply(iter, |ts| self.mk_tup(ts))
- }
-
- #[inline]
- pub fn mk_unit(self) -> Ty<'tcx> {
- self.types.unit
- }
-
- #[inline]
- pub fn mk_diverging_default(self) -> Ty<'tcx> {
- if self.features().never_type_fallback { self.types.never } else { self.types.unit }
- }
-
- #[inline]
- pub fn mk_fn_def(
- self,
- def_id: DefId,
- substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
- ) -> Ty<'tcx> {
- let substs = self.check_and_mk_substs(def_id, substs);
- self.mk_ty_from_kind(FnDef(def_id, substs))
- }
-
#[inline(always)]
pub(crate) fn check_and_mk_substs(
self,
@@ -1872,222 +1645,36 @@ impl<'tcx> TyCtxt<'tcx> {
}
#[inline]
- pub fn mk_fn_ptr(self, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(FnPtr(fty))
- }
-
- #[inline]
- pub fn mk_dynamic(
- self,
- obj: &'tcx List<PolyExistentialPredicate<'tcx>>,
- reg: ty::Region<'tcx>,
- repr: DynKind,
- ) -> Ty<'tcx> {
- self.mk_ty_from_kind(Dynamic(obj, reg, repr))
- }
-
- #[inline]
- pub fn mk_projection(
- self,
- item_def_id: DefId,
- substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
- ) -> Ty<'tcx> {
- self.mk_alias(ty::Projection, self.mk_alias_ty(item_def_id, substs))
- }
-
- #[inline]
- pub fn mk_closure(self, closure_id: DefId, closure_substs: SubstsRef<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(Closure(closure_id, closure_substs))
- }
-
- #[inline]
- pub fn mk_generator(
- self,
- id: DefId,
- generator_substs: SubstsRef<'tcx>,
- movability: hir::Movability,
- ) -> Ty<'tcx> {
- self.mk_ty_from_kind(Generator(id, generator_substs, movability))
- }
-
- #[inline]
- pub fn mk_generator_witness(self, types: ty::Binder<'tcx, &'tcx List<Ty<'tcx>>>) -> Ty<'tcx> {
- self.mk_ty_from_kind(GeneratorWitness(types))
- }
-
- /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
- pub fn mk_task_context(self) -> Ty<'tcx> {
- let context_did = self.require_lang_item(LangItem::Context, None);
- let context_adt_ref = self.adt_def(context_did);
- let context_substs = self.mk_substs(&[self.lifetimes.re_erased.into()]);
- let context_ty = self.mk_adt(context_adt_ref, context_substs);
- self.mk_mut_ref(self.lifetimes.re_erased, context_ty)
- }
-
- #[inline]
- pub fn mk_generator_witness_mir(self, id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
- self.mk_ty_from_kind(GeneratorWitnessMIR(id, substs))
- }
-
- #[inline]
- pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> {
- self.intern_const(ty::ConstData { kind: kind.into(), ty })
- }
-
- #[inline]
- pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> {
- // Use a pre-interned one when possible.
- self.types
- .ty_vars
- .get(v.as_usize())
- .copied()
- .unwrap_or_else(|| self.mk_ty_from_kind(Infer(TyVar(v))))
- }
-
- #[inline]
- pub fn mk_int_var(self, v: IntVid) -> Ty<'tcx> {
- self.mk_ty_from_kind(Infer(IntVar(v)))
- }
-
- #[inline]
- pub fn mk_float_var(self, v: FloatVid) -> Ty<'tcx> {
- self.mk_ty_from_kind(Infer(FloatVar(v)))
- }
-
- #[inline]
- pub fn mk_fresh_ty(self, n: u32) -> Ty<'tcx> {
- // Use a pre-interned one when possible.
- self.types
- .fresh_tys
- .get(n as usize)
- .copied()
- .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshTy(n))))
- }
-
- #[inline]
- pub fn mk_fresh_int_ty(self, n: u32) -> Ty<'tcx> {
- // Use a pre-interned one when possible.
- self.types
- .fresh_int_tys
- .get(n as usize)
- .copied()
- .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshIntTy(n))))
- }
-
- #[inline]
- pub fn mk_fresh_float_ty(self, n: u32) -> Ty<'tcx> {
- // Use a pre-interned one when possible.
- self.types
- .fresh_float_tys
- .get(n as usize)
- .copied()
- .unwrap_or_else(|| self.mk_ty_from_kind(Infer(ty::FreshFloatTy(n))))
+ pub fn mk_ct_from_kind(self, kind: ty::ConstKind<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ self.intern_const(ty::ConstData { kind, ty })
}
+ // Avoid this in favour of more specific `Ty::new_*` methods, where possible.
+ #[allow(rustc::usage_of_ty_tykind)]
#[inline]
- pub fn mk_ty_param(self, index: u32, name: Symbol) -> Ty<'tcx> {
- self.mk_ty_from_kind(Param(ParamTy { index, name }))
+ pub fn mk_ty_from_kind(self, st: TyKind<'tcx>) -> Ty<'tcx> {
+ self.interners.intern_ty(
+ st,
+ self.sess,
+ // This is only used to create a stable hashing context.
+ &self.untracked,
+ )
}
pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
match param.kind {
GenericParamDefKind::Lifetime => {
- self.mk_re_early_bound(param.to_early_bound_region_data()).into()
- }
- GenericParamDefKind::Type { .. } => self.mk_ty_param(param.index, param.name).into(),
- GenericParamDefKind::Const { .. } => self
- .mk_const(
- ParamConst { index: param.index, name: param.name },
- self.type_of(param.def_id)
- .no_bound_vars()
- .expect("const parameter types cannot be generic"),
- )
- .into(),
- }
- }
-
- #[inline]
- pub fn mk_bound(self, index: ty::DebruijnIndex, bound_ty: ty::BoundTy) -> Ty<'tcx> {
- self.mk_ty_from_kind(Bound(index, bound_ty))
- }
-
- #[inline]
- pub fn mk_placeholder(self, placeholder: ty::PlaceholderType) -> Ty<'tcx> {
- self.mk_ty_from_kind(Placeholder(placeholder))
- }
-
- #[inline]
- pub fn mk_alias(self, kind: ty::AliasKind, alias_ty: ty::AliasTy<'tcx>) -> Ty<'tcx> {
- debug_assert_matches!(
- (kind, self.def_kind(alias_ty.def_id)),
- (ty::Opaque, DefKind::OpaqueTy)
- | (ty::Projection | ty::Inherent, DefKind::AssocTy)
- | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
- );
- self.mk_ty_from_kind(Alias(kind, alias_ty))
- }
-
- #[inline]
- pub fn mk_opaque(self, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
- self.mk_alias(ty::Opaque, self.mk_alias_ty(def_id, substs))
- }
-
- #[inline]
- pub fn mk_re_early_bound(self, early_bound_region: ty::EarlyBoundRegion) -> Region<'tcx> {
- self.intern_region(ty::ReEarlyBound(early_bound_region))
- }
-
- #[inline]
- pub fn mk_re_late_bound(
- self,
- debruijn: ty::DebruijnIndex,
- bound_region: ty::BoundRegion,
- ) -> Region<'tcx> {
- // Use a pre-interned one when possible.
- if let ty::BoundRegion { var, kind: ty::BrAnon(None) } = bound_region
- && let Some(inner) = self.lifetimes.re_late_bounds.get(debruijn.as_usize())
- && let Some(re) = inner.get(var.as_usize()).copied()
- {
- re
- } else {
- self.intern_region(ty::ReLateBound(debruijn, bound_region))
- }
- }
-
- #[inline]
- pub fn mk_re_free(self, scope: DefId, bound_region: ty::BoundRegionKind) -> Region<'tcx> {
- self.intern_region(ty::ReFree(ty::FreeRegion { scope, bound_region }))
- }
-
- #[inline]
- pub fn mk_re_var(self, v: ty::RegionVid) -> Region<'tcx> {
- // Use a pre-interned one when possible.
- self.lifetimes
- .re_vars
- .get(v.as_usize())
- .copied()
- .unwrap_or_else(|| self.intern_region(ty::ReVar(v)))
- }
-
- #[inline]
- pub fn mk_re_placeholder(self, placeholder: ty::PlaceholderRegion) -> Region<'tcx> {
- self.intern_region(ty::RePlaceholder(placeholder))
- }
-
- // Avoid this in favour of more specific `mk_re_*` methods, where possible,
- // to avoid the cost of the `match`.
- pub fn mk_region_from_kind(self, kind: ty::RegionKind<'tcx>) -> Region<'tcx> {
- match kind {
- ty::ReEarlyBound(region) => self.mk_re_early_bound(region),
- ty::ReLateBound(debruijn, region) => self.mk_re_late_bound(debruijn, region),
- ty::ReFree(ty::FreeRegion { scope, bound_region }) => {
- self.mk_re_free(scope, bound_region)
+ ty::Region::new_early_bound(self, param.to_early_bound_region_data()).into()
}
- ty::ReStatic => self.lifetimes.re_static,
- ty::ReVar(vid) => self.mk_re_var(vid),
- ty::RePlaceholder(region) => self.mk_re_placeholder(region),
- ty::ReErased => self.lifetimes.re_erased,
- ty::ReError(reported) => self.mk_re_error(reported),
+ GenericParamDefKind::Type { .. } => Ty::new_param(self, param.index, param.name).into(),
+ GenericParamDefKind::Const { .. } => ty::Const::new_param(
+ self,
+ ParamConst { index: param.index, name: param.name },
+ self.type_of(param.def_id)
+ .no_bound_vars()
+ .expect("const parameter types cannot be generic"),
+ )
+ .into(),
}
}
@@ -2146,11 +1733,11 @@ impl<'tcx> TyCtxt<'tcx> {
self.intern_poly_existential_predicates(eps)
}
- pub fn mk_predicates(self, preds: &[Predicate<'tcx>]) -> &'tcx List<Predicate<'tcx>> {
+ pub fn mk_clauses(self, clauses: &[Clause<'tcx>]) -> &'tcx List<Clause<'tcx>> {
// FIXME consider asking the input slice to be sorted to avoid
// re-interning permutations, in which case that would be asserted
// here.
- self.intern_predicates(preds)
+ self.intern_clauses(clauses)
}
pub fn mk_const_list_from_iter<I, T>(self, iter: I) -> T::Output
@@ -2196,12 +1783,12 @@ impl<'tcx> TyCtxt<'tcx> {
T::collect_and_apply(iter, |xs| self.mk_poly_existential_predicates(xs))
}
- pub fn mk_predicates_from_iter<I, T>(self, iter: I) -> T::Output
+ pub fn mk_clauses_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
- T: CollectAndApply<Predicate<'tcx>, &'tcx List<Predicate<'tcx>>>,
+ T: CollectAndApply<Clause<'tcx>, &'tcx List<Clause<'tcx>>>,
{
- T::collect_and_apply(iter, |xs| self.mk_predicates(xs))
+ T::collect_and_apply(iter, |xs| self.mk_clauses(xs))
}
pub fn mk_type_list_from_iter<I, T>(self, iter: I) -> T::Output
@@ -2403,10 +1990,18 @@ impl<'tcx> TyCtxt<'tcx> {
self.opt_local_def_id_to_hir_id(local_def_id).unwrap()
}
- pub fn trait_solver_next(self) -> bool {
+ pub fn next_trait_solver_globally(self) -> bool {
self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next
}
+ pub fn next_trait_solver_in_coherence(self) -> bool {
+ matches!(
+ self.sess.opts.unstable_opts.trait_solver,
+ rustc_session::config::TraitSolver::Next
+ | rustc_session::config::TraitSolver::NextCoherence
+ )
+ }
+
pub fn lower_impl_trait_in_trait_to_assoc_ty(self) -> bool {
self.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
}
@@ -2433,21 +2028,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
-impl<'tcx> TyCtxtAt<'tcx> {
- /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
- #[track_caller]
- pub fn ty_error_misc(self) -> Ty<'tcx> {
- self.tcx.ty_error_with_message(self.span, "TyKind::Error constructed but no error reported")
- }
-
- /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
- /// ensure it gets used.
- #[track_caller]
- pub fn ty_error_with_message(self, msg: impl Into<DiagnosticMessage>) -> Ty<'tcx> {
- self.tcx.ty_error_with_message(self.span, msg)
- }
-}
-
/// Parameter attributes that can only be determined by examining the body of a function instead
/// of just its signature.
///
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 6a29063b8..a0b17c374 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -1,5 +1,6 @@
//! Diagnostics related methods for `Ty`.
+use std::borrow::Cow;
use std::ops::ControlFlow;
use crate::ty::{
@@ -13,8 +14,8 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnostic
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-use rustc_hir::WherePredicate;
-use rustc_span::Span;
+use rustc_hir::{PredicateOrigin, WherePredicate};
+use rustc_span::{BytePos, Span};
use rustc_type_ir::sty::TyKind::*;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
@@ -155,10 +156,11 @@ enum SuggestChangingConstraintsMessage<'a> {
RestrictBoundFurther,
RestrictType { ty: &'a str },
RestrictTypeFurther { ty: &'a str },
- RemovingQSized,
+ RemoveMaybeUnsized,
+ ReplaceMaybeUnsizedWithSized,
}
-fn suggest_removing_unsized_bound(
+fn suggest_changing_unsized_bound(
generics: &hir::Generics<'_>,
suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
param: &hir::GenericParam<'_>,
@@ -182,12 +184,25 @@ fn suggest_removing_unsized_bound(
if poly.trait_ref.trait_def_id() != def_id {
continue;
}
- let sp = generics.span_for_bound_removal(where_pos, pos);
- suggestions.push((
- sp,
- String::new(),
- SuggestChangingConstraintsMessage::RemovingQSized,
- ));
+ if predicate.origin == PredicateOrigin::ImplTrait && predicate.bounds.len() == 1 {
+ // For `impl ?Sized` with no other bounds, suggest `impl Sized` instead.
+ let bound_span = bound.span();
+ if bound_span.can_be_used_for_suggestions() {
+ let question_span = bound_span.with_hi(bound_span.lo() + BytePos(1));
+ suggestions.push((
+ question_span,
+ String::new(),
+ SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized,
+ ));
+ }
+ } else {
+ let sp = generics.span_for_bound_removal(where_pos, pos);
+ suggestions.push((
+ sp,
+ String::new(),
+ SuggestChangingConstraintsMessage::RemoveMaybeUnsized,
+ ));
+ }
}
}
}
@@ -236,15 +251,12 @@ pub fn suggest_constraining_type_params<'a>(
{
let mut sized_constraints =
- constraints.drain_filter(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
- if let Some((constraint, def_id)) = sized_constraints.next() {
+ constraints.extract_if(|(_, def_id)| *def_id == tcx.lang_items().sized_trait());
+ if let Some((_, def_id)) = sized_constraints.next() {
applicability = Applicability::MaybeIncorrect;
- err.span_label(
- param.span,
- format!("this type parameter needs to be `{}`", constraint),
- );
- suggest_removing_unsized_bound(generics, &mut suggestions, param, def_id);
+ err.span_label(param.span, "this type parameter needs to be `Sized`");
+ suggest_changing_unsized_bound(generics, &mut suggestions, param, def_id);
}
}
@@ -384,22 +396,21 @@ pub fn suggest_constraining_type_params<'a>(
if suggestions.len() == 1 {
let (span, suggestion, msg) = suggestions.pop().unwrap();
-
- let s;
let msg = match msg {
SuggestChangingConstraintsMessage::RestrictBoundFurther => {
- "consider further restricting this bound"
+ Cow::from("consider further restricting this bound")
}
SuggestChangingConstraintsMessage::RestrictType { ty } => {
- s = format!("consider restricting type parameter `{}`", ty);
- &s
+ Cow::from(format!("consider restricting type parameter `{}`", ty))
}
SuggestChangingConstraintsMessage::RestrictTypeFurther { ty } => {
- s = format!("consider further restricting type parameter `{}`", ty);
- &s
+ Cow::from(format!("consider further restricting type parameter `{}`", ty))
+ }
+ SuggestChangingConstraintsMessage::RemoveMaybeUnsized => {
+ Cow::from("consider removing the `?Sized` bound to make the type parameter `Sized`")
}
- SuggestChangingConstraintsMessage::RemovingQSized => {
- "consider removing the `?Sized` bound to make the type parameter `Sized`"
+ SuggestChangingConstraintsMessage::ReplaceMaybeUnsizedWithSized => {
+ Cow::from("consider replacing `?Sized` with `Sized`")
}
};
@@ -548,7 +559,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for MakeSuggestableFolder<'tcx> {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
FnDef(def_id, substs) => {
- self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
+ Ty::new_fn_ptr(self.tcx, self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}
// FIXME(compiler-errors): We could replace these with infer, I guess.
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 49ab9b79e..c794c3fad 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -45,7 +45,6 @@ pub enum TypeError<'tcx> {
RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
- RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>),
RegionsPlaceholderMismatch,
Sorts(ExpectedFound<Ty<'tcx>>),
@@ -74,7 +73,6 @@ impl TypeError<'_> {
match self {
TypeError::RegionsDoesNotOutlive(_, _)
| TypeError::RegionsInsufficientlyPolymorphic(_, _)
- | TypeError::RegionsOverlyPolymorphic(_, _)
| TypeError::RegionsPlaceholderMismatch => true,
_ => false,
}
@@ -98,11 +96,6 @@ impl<'tcx> TypeError<'tcx> {
}
}
- let br_string = |br: ty::BoundRegionKind| match br {
- ty::BrNamed(_, name) => format!(" {}", name),
- _ => String::new(),
- };
-
match self {
CyclicTy(_) => "cyclic type of infinite size".into(),
CyclicConst(_) => "encountered a self-referencing constant".into(),
@@ -144,11 +137,6 @@ impl<'tcx> TypeError<'tcx> {
RegionsInsufficientlyPolymorphic(..) => {
"one type is more general than the other".into()
}
- RegionsOverlyPolymorphic(br, _) => format!(
- "expected concrete lifetime, found bound lifetime parameter{}",
- br_string(br)
- )
- .into(),
RegionsPlaceholderMismatch => "one type is more general than the other".into(),
ArgumentSorts(values, _) | Sorts(values) => {
let expected = values.expected.sort_string(tcx);
@@ -228,7 +216,6 @@ impl<'tcx> TypeError<'tcx> {
| FieldMisMatch(..)
| RegionsDoesNotOutlive(..)
| RegionsInsufficientlyPolymorphic(..)
- | RegionsOverlyPolymorphic(..)
| RegionsPlaceholderMismatch
| Traits(_)
| ProjectionMismatched(_)
@@ -313,6 +300,7 @@ impl<'tcx> Ty<'tcx> {
ty::Placeholder(..) => "higher-ranked type".into(),
ty::Bound(..) => "bound type variable".into(),
ty::Alias(ty::Projection | ty::Inherent, _) => "associated type".into(),
+ ty::Alias(ty::Weak, _) => "type alias".into(),
ty::Param(_) => "type parameter".into(),
ty::Alias(ty::Opaque, ..) => "opaque type".into(),
}
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index d64875a9f..ff3917947 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -178,7 +178,7 @@ impl FlagComputation {
&ty::Alias(kind, data) => {
self.add_flags(match kind {
- ty::Projection => TypeFlags::HAS_TY_PROJECTION,
+ ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION,
ty::Inherent => TypeFlags::HAS_TY_INHERENT,
ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
});
@@ -237,21 +237,24 @@ impl FlagComputation {
fn add_predicate_atom(&mut self, atom: ty::PredicateKind<'_>) {
match atom {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
self.add_substs(trait_pred.trait_ref.substs);
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(a, b))) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
+ a,
+ b,
+ ))) => {
self.add_region(a);
self.add_region(b);
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty,
region,
))) => {
self.add_ty(ty);
self.add_region(region);
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
self.add_const(ct);
self.add_ty(ty);
}
@@ -263,30 +266,27 @@ impl FlagComputation {
self.add_ty(a);
self.add_ty(b);
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_ty,
term,
})) => {
self.add_alias_ty(projection_ty);
self.add_term(term);
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
self.add_substs(slice::from_ref(&arg));
}
ty::PredicateKind::ObjectSafe(_def_id) => {}
ty::PredicateKind::ClosureKind(_def_id, substs, _kind) => {
self.add_substs(substs);
}
- ty::PredicateKind::ConstEvaluatable(uv) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
self.add_const(uv);
}
ty::PredicateKind::ConstEquate(expected, found) => {
self.add_const(expected);
self.add_const(found);
}
- ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
- self.add_ty(ty);
- }
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::AliasRelate(t1, t2, _) => {
self.add_term(t1);
diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs
index 25890eb15..77cf6bee7 100644
--- a/compiler/rustc_middle/src/ty/fold.rs
+++ b/compiler/rustc_middle/src/ty/fold.rs
@@ -213,7 +213,7 @@ where
// debruijn index. Then we adjust it to the
// correct depth.
assert_eq!(debruijn1, ty::INNERMOST);
- self.tcx.mk_re_late_bound(debruijn, br)
+ ty::Region::new_late_bound(self.tcx, debruijn, br)
} else {
region
}
@@ -328,7 +328,7 @@ impl<'tcx> TyCtxt<'tcx> {
T: TypeFoldable<TyCtxt<'tcx>>,
{
self.replace_late_bound_regions_uncached(value, |br| {
- self.mk_re_free(all_outlive_scope, br.kind)
+ ty::Region::new_free(self, all_outlive_scope, br.kind)
})
}
@@ -341,16 +341,21 @@ impl<'tcx> TyCtxt<'tcx> {
value,
FnMutDelegate {
regions: &mut |r: ty::BoundRegion| {
- self.mk_re_late_bound(
+ ty::Region::new_late_bound(
+ self,
ty::INNERMOST,
ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
)
},
types: &mut |t: ty::BoundTy| {
- self.mk_bound(ty::INNERMOST, ty::BoundTy { var: shift_bv(t.var), kind: t.kind })
+ Ty::new_bound(
+ self,
+ ty::INNERMOST,
+ ty::BoundTy { var: shift_bv(t.var), kind: t.kind },
+ )
},
consts: &mut |c, ty: Ty<'tcx>| {
- self.mk_const(ty::ConstKind::Bound(ty::INNERMOST, shift_bv(c)), ty)
+ ty::Const::new_bound(self, ty::INNERMOST, shift_bv(c), ty)
},
},
)
@@ -383,7 +388,7 @@ impl<'tcx> TyCtxt<'tcx> {
.or_insert_with(|| ty::BoundVariableKind::Region(ty::BrAnon(None)))
.expect_region();
let br = ty::BoundRegion { var, kind };
- self.tcx.mk_re_late_bound(ty::INNERMOST, br)
+ ty::Region::new_late_bound(self.tcx, ty::INNERMOST, br)
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
let entry = self.map.entry(bt.var);
@@ -392,14 +397,14 @@ impl<'tcx> TyCtxt<'tcx> {
let kind = entry
.or_insert_with(|| ty::BoundVariableKind::Ty(ty::BoundTyKind::Anon))
.expect_ty();
- self.tcx.mk_bound(ty::INNERMOST, BoundTy { var, kind })
+ Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
}
fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> {
let entry = self.map.entry(bv);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
- self.tcx.mk_const(ty::ConstKind::Bound(ty::INNERMOST, var), ty)
+ ty::Const::new_bound(self.tcx, ty::INNERMOST, var, ty)
}
}
@@ -451,7 +456,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
match *r {
ty::ReLateBound(debruijn, br) if debruijn >= self.current_index => {
let debruijn = debruijn.shifted_in(self.amount);
- self.tcx.mk_re_late_bound(debruijn, br)
+ ty::Region::new_late_bound(self.tcx, debruijn, br)
}
_ => r,
}
@@ -461,7 +466,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
match *ty.kind() {
ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
let debruijn = debruijn.shifted_in(self.amount);
- self.tcx.mk_bound(debruijn, bound_ty)
+ Ty::new_bound(self.tcx, debruijn, bound_ty)
}
_ if ty.has_vars_bound_at_or_above(self.current_index) => ty.super_fold_with(self),
@@ -474,7 +479,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Shifter<'tcx> {
&& debruijn >= self.current_index
{
let debruijn = debruijn.shifted_in(self.amount);
- self.tcx.mk_const(ty::ConstKind::Bound(debruijn, bound_ct), ct.ty())
+ ty::Const::new_bound(self.tcx, debruijn, bound_ct, ct.ty())
} else {
ct.super_fold_with(self)
}
@@ -492,7 +497,7 @@ pub fn shift_region<'tcx>(
) -> ty::Region<'tcx> {
match *region {
ty::ReLateBound(debruijn, br) if amount > 0 => {
- tcx.mk_re_late_bound(debruijn.shifted_in(amount), br)
+ ty::Region::new_late_bound(tcx, debruijn.shifted_in(amount), br)
}
_ => region,
}
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index b0ffe7829..6c7125c4c 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::Span;
-use super::{EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, Predicate, TyCtxt};
+use super::{Clause, EarlyBoundRegion, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
pub enum GenericParamDefKind {
@@ -100,11 +100,13 @@ impl GenericParamDef {
preceding_substs: &[ty::GenericArg<'tcx>],
) -> ty::GenericArg<'tcx> {
match &self.kind {
- ty::GenericParamDefKind::Lifetime => tcx.mk_re_error_misc().into(),
- ty::GenericParamDefKind::Type { .. } => tcx.ty_error_misc().into(),
- ty::GenericParamDefKind::Const { .. } => {
- tcx.const_error_misc(tcx.type_of(self.def_id).subst(tcx, preceding_substs)).into()
- }
+ ty::GenericParamDefKind::Lifetime => ty::Region::new_error_misc(tcx).into(),
+ ty::GenericParamDefKind::Type { .. } => Ty::new_misc_error(tcx).into(),
+ ty::GenericParamDefKind::Const { .. } => ty::Const::new_misc_error(
+ tcx,
+ tcx.type_of(self.def_id).subst(tcx, preceding_substs),
+ )
+ .into(),
}
}
}
@@ -133,6 +135,9 @@ pub struct Generics {
pub has_self: bool,
pub has_late_bound_regions: Option<Span>,
+
+ // The index of the host effect when substituted. (i.e. might be index to parent substs)
+ pub host_effect_index: Option<usize>,
}
impl<'tcx> Generics {
@@ -323,7 +328,7 @@ impl<'tcx> Generics {
#[derive(Copy, Clone, Default, Debug, TyEncodable, TyDecodable, HashStable)]
pub struct GenericPredicates<'tcx> {
pub parent: Option<DefId>,
- pub predicates: &'tcx [(Predicate<'tcx>, Span)],
+ pub predicates: &'tcx [(Clause<'tcx>, Span)],
}
impl<'tcx> GenericPredicates<'tcx> {
@@ -341,9 +346,8 @@ impl<'tcx> GenericPredicates<'tcx> {
&self,
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
- ) -> impl Iterator<Item = (Predicate<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
- {
- EarlyBinder(self.predicates).subst_iter_copied(tcx, substs)
+ ) -> impl Iterator<Item = (Clause<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator {
+ EarlyBinder::bind(self.predicates).subst_iter_copied(tcx, substs)
}
#[instrument(level = "debug", skip(self, tcx))]
@@ -358,7 +362,7 @@ impl<'tcx> GenericPredicates<'tcx> {
}
instantiated
.predicates
- .extend(self.predicates.iter().map(|(p, _)| EarlyBinder(*p).subst(tcx, substs)));
+ .extend(self.predicates.iter().map(|(p, _)| EarlyBinder::bind(*p).subst(tcx, substs)));
instantiated.spans.extend(self.predicates.iter().map(|(_, sp)| *sp));
}
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
index ac42d6e05..295cb1464 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/inhabited_predicate.rs
@@ -62,7 +62,18 @@ impl<'tcx> InhabitedPredicate<'tcx> {
Some(1..) => Ok(false),
},
Self::NotInModule(id) => in_module(id).map(|in_mod| !in_mod),
- Self::GenericType(_) => Ok(true),
+ // `t` may be a projection, for which `inhabited_predicate` returns a `GenericType`. As
+ // we have a param_env available, we can do better.
+ Self::GenericType(t) => {
+ let normalized_pred = tcx
+ .try_normalize_erasing_regions(param_env, t)
+ .map_or(self, |t| t.inhabited_predicate(tcx));
+ match normalized_pred {
+ // We don't have more information than we started with, so consider inhabited.
+ Self::GenericType(_) => Ok(true),
+ pred => pred.apply_inner(tcx, param_env, in_module),
+ }
+ }
Self::And([a, b]) => try_and(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
Self::Or([a, b]) => try_or(a, b, |x| x.apply_inner(tcx, param_env, in_module)),
}
@@ -158,8 +169,8 @@ impl<'tcx> InhabitedPredicate<'tcx> {
fn subst_opt(self, tcx: TyCtxt<'tcx>, substs: ty::SubstsRef<'tcx>) -> Option<Self> {
match self {
Self::ConstIsZero(c) => {
- let c = ty::EarlyBinder(c).subst(tcx, substs);
- let pred = match c.kind().try_to_target_usize(tcx) {
+ let c = ty::EarlyBinder::bind(c).subst(tcx, substs);
+ let pred = match c.try_to_target_usize(tcx) {
Some(0) => Self::True,
Some(1..) => Self::False,
None => Self::ConstIsZero(c),
@@ -167,7 +178,7 @@ impl<'tcx> InhabitedPredicate<'tcx> {
Some(pred)
}
Self::GenericType(t) => {
- Some(ty::EarlyBinder(t).subst(tcx, substs).inhabited_predicate(tcx))
+ Some(ty::EarlyBinder::bind(t).subst(tcx, substs).inhabited_predicate(tcx))
}
Self::And(&[a, b]) => match a.subst_opt(tcx, substs) {
None => b.subst_opt(tcx, substs).map(|b| a.and(tcx, b)),
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index 422350284..b92d84152 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -197,7 +197,7 @@ fn inhabited_predicate_type<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> InhabitedP
// If we can evaluate the array length before having a `ParamEnv`, then
// we can simplify the predicate. This is an optimization.
- Array(ty, len) => match len.kind().try_to_target_usize(tcx) {
+ Array(ty, len) => match len.try_to_target_usize(tcx) {
Some(0) => InhabitedPredicate::True,
Some(1..) => ty.inhabited_predicate(tcx),
None => ty.inhabited_predicate(tcx).or(tcx, InhabitedPredicate::ConstIsZero(len)),
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index e641d1ef1..ae57e954f 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -410,8 +410,8 @@ impl<'tcx> Instance<'tcx> {
) -> Instance<'tcx> {
match ty::Instance::resolve(tcx, param_env, def_id, substs) {
Ok(Some(instance)) => instance,
- _ => bug!(
- "failed to resolve instance for {}",
+ instance => bug!(
+ "failed to resolve instance for {}: {instance:#?}",
tcx.def_path_str_with_substs(def_id, substs)
),
}
@@ -552,7 +552,7 @@ impl<'tcx> Instance<'tcx> {
tcx.codegen_fn_attrs(closure_did).flags.contains(CodegenFnAttrFlags::TRACK_CALLER);
let def = ty::InstanceDef::ClosureOnceShim { call_once, track_caller };
- let self_ty = tcx.mk_closure(closure_did, substs);
+ let self_ty = Ty::new_closure(tcx, closure_did, substs);
let sig = substs.as_closure().sig();
let sig =
@@ -586,7 +586,7 @@ impl<'tcx> Instance<'tcx> {
if let Some(substs) = self.substs_for_mir_body() {
v.subst(tcx, substs)
} else {
- v.skip_binder()
+ v.subst_identity()
}
}
@@ -680,7 +680,7 @@ fn polymorphize<'tcx>(
if substs == polymorphized_substs {
ty
} else {
- self.tcx.mk_closure(def_id, polymorphized_substs)
+ Ty::new_closure(self.tcx, def_id, polymorphized_substs)
}
}
ty::Generator(def_id, substs, movability) => {
@@ -689,7 +689,7 @@ fn polymorphize<'tcx>(
if substs == polymorphized_substs {
ty
} else {
- self.tcx.mk_generator(def_id, polymorphized_substs, movability)
+ Ty::new_generator(self.tcx, def_id, polymorphized_substs, movability)
}
}
_ => ty.super_fold_with(self),
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index b5a743cfe..d95b05ef7 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1,8 +1,9 @@
-use crate::fluent_generated as fluent;
+use crate::error::UnsupportedFnAbi;
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::query::TyCtxtAt;
use crate::ty::normalize_erasing_regions::NormalizationError;
-use crate::ty::{self, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
+use crate::ty::{self, ConstKind, ReprOptions, Ty, TyCtxt, TypeVisitableExt};
+use rustc_error_messages::DiagnosticMessage;
use rustc_errors::{DiagnosticBuilder, Handler, IntoDiagnostic};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@@ -14,7 +15,7 @@ use rustc_target::abi::call::FnAbi;
use rustc_target::abi::*;
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
-use std::cmp::{self};
+use std::cmp;
use std::fmt;
use std::num::NonZeroUsize;
use std::ops::Bound;
@@ -132,7 +133,7 @@ impl PrimitiveExt for Primitive {
F32 => tcx.types.f32,
F64 => tcx.types.f64,
// FIXME(erikdesjardins): handle non-default addrspace ptr sizes
- Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
+ Pointer(_) => Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)),
}
}
@@ -214,29 +215,29 @@ pub enum LayoutError<'tcx> {
Cycle,
}
-impl IntoDiagnostic<'_, !> for LayoutError<'_> {
- fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
- let mut diag = handler.struct_fatal("");
+impl<'tcx> LayoutError<'tcx> {
+ pub fn diagnostic_message(&self) -> DiagnosticMessage {
+ use crate::fluent_generated::*;
+ use LayoutError::*;
+ match self {
+ Unknown(_) => middle_unknown_layout,
+ SizeOverflow(_) => middle_values_too_big,
+ NormalizationFailure(_, _) => middle_cannot_be_normalized,
+ Cycle => middle_cycle,
+ }
+ }
+ pub fn into_diagnostic(self) -> crate::error::LayoutError<'tcx> {
+ use crate::error::LayoutError as E;
+ use LayoutError::*;
match self {
- LayoutError::Unknown(ty) => {
- diag.set_arg("ty", ty);
- diag.set_primary_message(fluent::middle_unknown_layout);
- }
- LayoutError::SizeOverflow(ty) => {
- diag.set_arg("ty", ty);
- diag.set_primary_message(fluent::middle_values_too_big);
- }
- LayoutError::NormalizationFailure(ty, e) => {
- diag.set_arg("ty", ty);
- diag.set_arg("failure_ty", e.get_type_for_failure());
- diag.set_primary_message(fluent::middle_cannot_be_normalized);
- }
- LayoutError::Cycle => {
- diag.set_primary_message(fluent::middle_cycle);
+ Unknown(ty) => E::Unknown { ty },
+ SizeOverflow(ty) => E::Overflow { ty },
+ NormalizationFailure(ty, e) => {
+ E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
}
+ Cycle => E::Cycle,
}
- diag
}
}
@@ -309,7 +310,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- ) -> Result<SizeSkeleton<'tcx>, LayoutError<'tcx>> {
+ ) -> Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>> {
debug_assert!(!ty.has_non_region_infer());
// First try computing a static layout.
@@ -317,7 +318,13 @@ impl<'tcx> SizeSkeleton<'tcx> {
Ok(layout) => {
return Ok(SizeSkeleton::Known(layout.size));
}
- Err(err) => err,
+ Err(err @ LayoutError::Unknown(_)) => err,
+ // We can't extract SizeSkeleton info from other layout errors
+ Err(
+ e @ LayoutError::Cycle
+ | e @ LayoutError::SizeOverflow(_)
+ | e @ LayoutError::NormalizationFailure(..),
+ ) => return Err(e),
};
match *ty.kind() {
@@ -330,11 +337,8 @@ impl<'tcx> SizeSkeleton<'tcx> {
Ok(SizeSkeleton::Pointer { non_zero, tail: tcx.erase_regions(tail) })
}
_ => bug!(
- "SizeSkeleton::compute({}): layout errored ({}), yet \
- tail `{}` is not a type parameter or a projection",
- ty,
- err,
- tail
+ "SizeSkeleton::compute({ty}): layout errored ({err:?}), yet \
+ tail `{tail}` is not a type parameter or a projection",
),
}
}
@@ -349,13 +353,13 @@ impl<'tcx> SizeSkeleton<'tcx> {
let size = s
.bytes()
.checked_mul(c)
- .ok_or_else(|| LayoutError::SizeOverflow(ty))?;
+ .ok_or_else(|| &*tcx.arena.alloc(LayoutError::SizeOverflow(ty)))?;
return Ok(SizeSkeleton::Known(Size::from_bytes(size)));
}
let len = tcx.expand_abstract_consts(len);
let prev = ty::Const::from_target_usize(tcx, s.bytes());
let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, prev) else {
- return Err(LayoutError::SizeOverflow(ty));
+ return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty)));
};
Ok(SizeSkeleton::Generic(gen_size))
}
@@ -363,7 +367,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
SizeSkeleton::Generic(g) => {
let len = tcx.expand_abstract_consts(len);
let Some(gen_size) = mul_sorted_consts(tcx, param_env, len, g) else {
- return Err(LayoutError::SizeOverflow(ty));
+ return Err(tcx.arena.alloc(LayoutError::SizeOverflow(ty)));
};
Ok(SizeSkeleton::Generic(gen_size))
}
@@ -476,13 +480,11 @@ fn mul_sorted_consts<'tcx>(
b: ty::Const<'tcx>,
) -> Option<ty::Const<'tcx>> {
use crate::mir::BinOp::Mul;
- use ty::ConstKind::Expr;
- use ty::Expr::Binop;
let mut work = vec![a, b];
let mut done = vec![];
while let Some(n) = work.pop() {
- if let Expr(Binop(Mul, l, r)) = n.kind() {
+ if let ConstKind::Expr(ty::Expr::Binop(Mul, l, r)) = n.kind() {
work.push(l);
work.push(r)
} else {
@@ -513,7 +515,7 @@ fn mul_sorted_consts<'tcx>(
done.sort_unstable();
// create a single tree from the buffer
- done.into_iter().reduce(|acc, n| tcx.mk_const(Expr(Binop(Mul, n, acc)), n.ty()))
+ done.into_iter().reduce(|acc, n| ty::Const::new_expr(tcx, ty::Expr::Binop(Mul, n, acc), n.ty()))
}
pub trait HasTyCtxt<'tcx>: HasDataLayout {
@@ -668,7 +670,7 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
MaybeResult::from(
tcx.layout_of(self.param_env().and(ty))
- .map_err(|err| self.handle_layout_err(err, span, ty)),
+ .map_err(|err| self.handle_layout_err(*err, span, ty)),
)
}
}
@@ -676,16 +678,21 @@ pub trait LayoutOf<'tcx>: LayoutOfHelpers<'tcx> {
impl<'tcx, C: LayoutOfHelpers<'tcx>> LayoutOf<'tcx> for C {}
impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxt<'tcx>> {
- type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
+ type LayoutOfResult = Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>>;
#[inline]
- fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
- err
+ fn handle_layout_err(
+ &self,
+ err: LayoutError<'tcx>,
+ _: Span,
+ _: Ty<'tcx>,
+ ) -> &'tcx LayoutError<'tcx> {
+ self.tcx.arena.alloc(err)
}
}
impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxtAt<'tcx>> {
- type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
+ type LayoutOfResult = Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>>;
#[inline]
fn layout_tcx_at_span(&self) -> Span {
@@ -693,8 +700,13 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LayoutCx<'tcx, TyCtxtAt<'tcx>> {
}
#[inline]
- fn handle_layout_err(&self, err: LayoutError<'tcx>, _: Span, _: Ty<'tcx>) -> LayoutError<'tcx> {
- err
+ fn handle_layout_err(
+ &self,
+ err: LayoutError<'tcx>,
+ _: Span,
+ _: Ty<'tcx>,
+ ) -> &'tcx LayoutError<'tcx> {
+ self.tcx.arena.alloc(err)
}
}
@@ -798,11 +810,11 @@ where
// (which may have no non-DST form), and will work as long
// as the `Abi` or `FieldsShape` is checked by users.
if i == 0 {
- let nil = tcx.mk_unit();
+ let nil = Ty::new_unit(tcx);
let unit_ptr_ty = if this.ty.is_unsafe_ptr() {
- tcx.mk_mut_ptr(nil)
+ Ty::new_mut_ptr(tcx, nil)
} else {
- tcx.mk_mut_ref(tcx.lifetimes.re_static, nil)
+ Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, nil)
};
// NOTE(eddyb) using an empty `ParamEnv`, and `unwrap`-ing
@@ -815,7 +827,11 @@ where
}
let mk_dyn_vtable = || {
- tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3))
+ Ty::new_imm_ref(
+ tcx,
+ tcx.lifetimes.re_static,
+ Ty::new_array(tcx, tcx.types.usize, 3),
+ )
/* FIXME: use actual fn pointers
Warning: naively computing the number of entries in the
vtable by counting the methods on the trait + methods on
@@ -824,9 +840,9 @@ where
Increase this counter if you tried to implement this but
failed to do it without duplicating a lot of code from
other places in the compiler: 2
- tcx.mk_tup(&[
- tcx.mk_array(tcx.types.usize, 3),
- tcx.mk_array(Option<fn()>),
+ Ty::new_tup(tcx,&[
+ Ty::new_array(tcx,tcx.types.usize, 3),
+ Ty::new_array(tcx,Option<fn()>),
])
*/
};
@@ -838,7 +854,7 @@ where
{
let metadata = tcx.normalize_erasing_regions(
cx.param_env(),
- tcx.mk_projection(metadata_def_id, [pointee]),
+ Ty::new_projection(tcx,metadata_def_id, [pointee]),
);
// Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it
@@ -913,15 +929,14 @@ where
ty::Dynamic(_, _, ty::DynStar) => {
if i == 0 {
- TyMaybeWithLayout::Ty(tcx.mk_mut_ptr(tcx.types.unit))
+ TyMaybeWithLayout::Ty(Ty::new_mut_ptr(tcx, tcx.types.unit))
} else if i == 1 {
// FIXME(dyn-star) same FIXME as above applies here too
- TyMaybeWithLayout::Ty(
- tcx.mk_imm_ref(
- tcx.lifetimes.re_static,
- tcx.mk_array(tcx.types.usize, 3),
- ),
- )
+ TyMaybeWithLayout::Ty(Ty::new_imm_ref(
+ tcx,
+ tcx.lifetimes.re_static,
+ Ty::new_array(tcx, tcx.types.usize, 3),
+ ))
} else {
bug!("no field {i} on dyn*")
}
@@ -940,12 +955,8 @@ where
TyMaybeWithLayout::Ty(field_ty) => {
cx.tcx().layout_of(cx.param_env().and(field_ty)).unwrap_or_else(|e| {
bug!(
- "failed to get layout for `{}`: {},\n\
- despite it being a field (#{}) of an existing layout: {:#?}",
- field_ty,
- e,
- i,
- this
+ "failed to get layout for `{field_ty}`: {e:?},\n\
+ despite it being a field (#{i}) of an existing layout: {this:#?}",
)
})
}
@@ -970,10 +981,8 @@ where
})
}
ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
- tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: None,
+ tcx.layout_of(param_env.and(Ty::new_fn_ptr(tcx, fn_sig))).ok().map(|layout| {
+ PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
})
}
ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
@@ -1101,12 +1110,11 @@ where
///
/// This takes two primary parameters:
///
-/// * `codegen_fn_attr_flags` - these are flags calculated as part of the
-/// codegen attrs for a defined function. For function pointers this set of
-/// flags is the empty set. This is only applicable for Rust-defined
-/// functions, and generally isn't needed except for small optimizations where
-/// we try to say a function which otherwise might look like it could unwind
-/// doesn't actually unwind (such as for intrinsics and such).
+/// * `fn_def_id` - the `DefId` of the function. If this is provided then we can
+/// determine more precisely if the function can unwind. If this is not provided
+/// then we will only infer whether the function can unwind or not based on the
+/// ABI of the function. For example, a function marked with `#[rustc_nounwind]`
+/// is known to not unwind even if it's using Rust ABI.
///
/// * `abi` - this is the ABI that the function is defined with. This is the
/// primary factor for determining whether a function can unwind or not.
@@ -1138,11 +1146,6 @@ where
/// aborts the process.
/// * This affects whether functions have the LLVM `nounwind` attribute, which
/// affects various optimizations and codegen.
-///
-/// FIXME: this is actually buggy with respect to Rust functions. Rust functions
-/// compiled with `-Cpanic=unwind` and referenced from another crate compiled
-/// with `-Cpanic=abort` will look like they can't unwind when in fact they
-/// might (from a foreign exception or similar).
#[inline]
#[tracing::instrument(level = "debug", skip(tcx))]
pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
@@ -1250,33 +1253,18 @@ pub enum FnAbiError<'tcx> {
AdjustForForeignAbi(call::AdjustForForeignAbiError),
}
-impl<'tcx> From<LayoutError<'tcx>> for FnAbiError<'tcx> {
- fn from(err: LayoutError<'tcx>) -> Self {
- Self::Layout(err)
- }
-}
-
-impl From<call::AdjustForForeignAbiError> for FnAbiError<'_> {
- fn from(err: call::AdjustForForeignAbiError) -> Self {
- Self::AdjustForForeignAbi(err)
- }
-}
-
-impl<'tcx> fmt::Display for FnAbiError<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl<'a, 'b> IntoDiagnostic<'a, !> for FnAbiError<'b> {
+ fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, !> {
match self {
- Self::Layout(err) => err.fmt(f),
- Self::AdjustForForeignAbi(err) => err.fmt(f),
+ Self::Layout(e) => e.into_diagnostic().into_diagnostic(handler),
+ Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
+ arch,
+ abi,
+ }) => UnsupportedFnAbi { arch, abi: abi.name() }.into_diagnostic(handler),
}
}
}
-impl IntoDiagnostic<'_, !> for FnAbiError<'_> {
- fn into_diagnostic(self, handler: &Handler) -> DiagnosticBuilder<'_, !> {
- handler.struct_fatal(self.to_string())
- }
-}
-
// FIXME(eddyb) maybe use something like this for an unified `fn_abi_of`, not
// just for error handling.
#[derive(Debug)]
@@ -1324,7 +1312,7 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
let tcx = self.tcx().at(span);
MaybeResult::from(tcx.fn_abi_of_fn_ptr(self.param_env().and((sig, extra_args))).map_err(
- |err| self.handle_fn_abi_err(err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
+ |err| self.handle_fn_abi_err(*err, span, FnAbiRequest::OfFnPtr { sig, extra_args }),
))
}
@@ -1351,7 +1339,11 @@ pub trait FnAbiOf<'tcx>: FnAbiOfHelpers<'tcx> {
// However, we don't do this early in order to avoid calling
// `def_span` unconditionally (which may have a perf penalty).
let span = if !span.is_dummy() { span } else { tcx.def_span(instance.def_id()) };
- self.handle_fn_abi_err(err, span, FnAbiRequest::OfInstance { instance, extra_args })
+ self.handle_fn_abi_err(
+ *err,
+ span,
+ FnAbiRequest::OfInstance { instance, extra_args },
+ )
}),
)
}
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index a8d0dca37..aa8bfd317 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -53,7 +53,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{ExpnId, ExpnKind, Span};
use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
pub use rustc_target::abi::{ReprFlags, ReprOptions};
-use rustc_type_ir::WithCachedTypeInfo;
pub use subst::*;
pub use vtable::*;
@@ -67,6 +66,10 @@ use std::{fmt, str};
pub use crate::ty::diagnostics::*;
pub use rustc_type_ir::AliasKind::*;
+pub use rustc_type_ir::ConstKind::{
+ Bound as BoundCt, Error as ErrorCt, Expr as ExprCt, Infer as InferCt, Param as ParamCt,
+ Placeholder as PlaceholderCt, Unevaluated, Value,
+};
pub use rustc_type_ir::DynKind::*;
pub use rustc_type_ir::InferTy::*;
pub use rustc_type_ir::RegionKind::*;
@@ -82,7 +85,7 @@ pub use self::closure::{
CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{
- Const, ConstData, ConstInt, ConstKind, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
+ Const, ConstData, ConstInt, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
};
pub use self::context::{
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt, TyCtxtFeed,
@@ -94,7 +97,7 @@ pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::BoundRegionKind::*;
pub use self::sty::{
AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar,
- BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstVid,
+ BoundVariableKind, CanonicalPolyFnSig, ClosureSubsts, ClosureSubstsParts, ConstKind, ConstVid,
EarlyBoundRegion, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
FreeRegion, GenSig, GeneratorSubsts, GeneratorSubstsParts, InlineConstSubsts,
InlineConstSubstsParts, ParamConst, ParamTy, PolyExistentialPredicate,
@@ -145,6 +148,7 @@ mod opaque_types;
mod parameterized;
mod rvalue_scopes;
mod structural_impls;
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod sty;
mod typeck_results;
@@ -456,6 +460,11 @@ impl ty::EarlyBoundRegion {
}
}
+/// A statement that can be proven by a trait solver. This includes things that may
+/// show up in where clauses, such as trait predicates and projection predicates,
+/// and also things that are emitted as part of type checking such as `ObjectSafe`
+/// predicate which is emitted when a type is coerced to a trait object.
+///
/// Use this rather than `PredicateKind`, whenever possible.
#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable)]
#[rustc_pass_by_value]
@@ -487,11 +496,11 @@ impl<'tcx> Predicate<'tcx> {
let kind = self
.kind()
.map_bound(|kind| match kind {
- PredicateKind::Clause(Clause::Trait(TraitPredicate {
+ PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
trait_ref,
constness,
polarity,
- })) => Some(PredicateKind::Clause(Clause::Trait(TraitPredicate {
+ })) => Some(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
trait_ref,
constness,
polarity: polarity.flip()?,
@@ -505,10 +514,10 @@ impl<'tcx> Predicate<'tcx> {
}
pub fn without_const(mut self, tcx: TyCtxt<'tcx>) -> Self {
- if let PredicateKind::Clause(Clause::Trait(TraitPredicate { trait_ref, constness, polarity })) = self.kind().skip_binder()
+ if let PredicateKind::Clause(ClauseKind::Trait(TraitPredicate { trait_ref, constness, polarity })) = self.kind().skip_binder()
&& constness != BoundConstness::NotConst
{
- self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Clause(Clause::Trait(TraitPredicate {
+ self = tcx.mk_predicate(self.kind().rebind(PredicateKind::Clause(ClauseKind::Trait(TraitPredicate {
trait_ref,
constness: BoundConstness::NotConst,
polarity,
@@ -520,10 +529,10 @@ impl<'tcx> Predicate<'tcx> {
#[instrument(level = "debug", skip(tcx), ret)]
pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool {
match self.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
tcx.trait_is_coinductive(data.def_id())
}
- ty::PredicateKind::WellFormed(_) => true,
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => true,
_ => false,
}
}
@@ -536,21 +545,20 @@ impl<'tcx> Predicate<'tcx> {
#[inline]
pub fn allow_normalization(self) -> bool {
match self.kind().skip_binder() {
- PredicateKind::WellFormed(_) => false,
- PredicateKind::Clause(Clause::Trait(_))
- | PredicateKind::Clause(Clause::RegionOutlives(_))
- | PredicateKind::Clause(Clause::TypeOutlives(_))
- | PredicateKind::Clause(Clause::Projection(_))
- | PredicateKind::Clause(Clause::ConstArgHasType(..))
+ PredicateKind::Clause(ClauseKind::WellFormed(_)) => false,
+ PredicateKind::Clause(ClauseKind::Trait(_))
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(_))
+ | PredicateKind::Clause(ClauseKind::TypeOutlives(_))
+ | PredicateKind::Clause(ClauseKind::Projection(_))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::AliasRelate(..)
| PredicateKind::ObjectSafe(_)
| PredicateKind::ClosureKind(_, _, _)
| PredicateKind::Subtype(_)
| PredicateKind::Coerce(_)
- | PredicateKind::ConstEvaluatable(_)
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_))
| PredicateKind::ConstEquate(_, _)
- | PredicateKind::Ambiguous
- | PredicateKind::TypeWellFormedFromEnv(_) => true,
+ | PredicateKind::Ambiguous => true,
}
}
}
@@ -561,11 +569,77 @@ impl rustc_errors::IntoDiagnosticArg for Predicate<'_> {
}
}
+impl rustc_errors::IntoDiagnosticArg for Clause<'_> {
+ fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+ rustc_errors::DiagnosticArgValue::Str(std::borrow::Cow::Owned(self.to_string()))
+ }
+}
+
+/// A subset of predicates which can be assumed by the trait solver. They show up in
+/// an item's where clauses, hence the name `Clause`, and may either be user-written
+/// (such as traits) or may be inserted during lowering.
+#[derive(Clone, Copy, PartialEq, Eq, Hash, HashStable)]
+#[rustc_pass_by_value]
+pub struct Clause<'tcx>(Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>);
+
+impl<'tcx> Clause<'tcx> {
+ pub fn as_predicate(self) -> Predicate<'tcx> {
+ Predicate(self.0)
+ }
+
+ pub fn kind(self) -> Binder<'tcx, ClauseKind<'tcx>> {
+ self.0.internee.map_bound(|kind| match kind {
+ PredicateKind::Clause(clause) => clause,
+ _ => unreachable!(),
+ })
+ }
+
+ pub fn as_trait_clause(self) -> Option<Binder<'tcx, TraitPredicate<'tcx>>> {
+ let clause = self.kind();
+ if let ty::ClauseKind::Trait(trait_clause) = clause.skip_binder() {
+ Some(clause.rebind(trait_clause))
+ } else {
+ None
+ }
+ }
+
+ pub fn as_projection_clause(self) -> Option<Binder<'tcx, ProjectionPredicate<'tcx>>> {
+ let clause = self.kind();
+ if let ty::ClauseKind::Projection(projection_clause) = clause.skip_binder() {
+ Some(clause.rebind(projection_clause))
+ } else {
+ None
+ }
+ }
+
+ pub fn as_type_outlives_clause(self) -> Option<Binder<'tcx, TypeOutlivesPredicate<'tcx>>> {
+ let clause = self.kind();
+ if let ty::ClauseKind::TypeOutlives(o) = clause.skip_binder() {
+ Some(clause.rebind(o))
+ } else {
+ None
+ }
+ }
+
+ pub fn as_region_outlives_clause(self) -> Option<Binder<'tcx, RegionOutlivesPredicate<'tcx>>> {
+ let clause = self.kind();
+ if let ty::ClauseKind::RegionOutlives(o) = clause.skip_binder() {
+ Some(clause.rebind(o))
+ } else {
+ None
+ }
+ }
+
+ pub fn without_const(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ self.as_predicate().without_const(tcx).expect_clause()
+ }
+}
+
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
/// A clause is something that can appear in where bounds or be inferred
/// by implied bounds.
-pub enum Clause<'tcx> {
+pub enum ClauseKind<'tcx> {
/// Corresponds to `where Foo: Bar<A, B, C>`. `Foo` here would be
/// the `Self` type of the trait reference and `A`, `B`, and `C`
/// would be the type parameters.
@@ -584,16 +658,19 @@ pub enum Clause<'tcx> {
/// Ensures that a const generic argument to a parameter `const N: u8`
/// is of type `u8`.
ConstArgHasType(Const<'tcx>, Ty<'tcx>),
+
+ /// No syntax: `T` well-formed.
+ WellFormed(GenericArg<'tcx>),
+
+ /// Constant initializer must evaluate successfully.
+ ConstEvaluatable(ty::Const<'tcx>),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub enum PredicateKind<'tcx> {
/// Prove a clause
- Clause(Clause<'tcx>),
-
- /// No syntax: `T` well-formed.
- WellFormed(GenericArg<'tcx>),
+ Clause(ClauseKind<'tcx>),
/// Trait must be object-safe.
ObjectSafe(DefId),
@@ -620,22 +697,14 @@ pub enum PredicateKind<'tcx> {
/// logic.
Coerce(CoercePredicate<'tcx>),
- /// Constant initializer must evaluate successfully.
- ConstEvaluatable(ty::Const<'tcx>),
-
/// Constants must be equal. The first component is the const that is expected.
ConstEquate(Const<'tcx>, Const<'tcx>),
- /// Represents a type found in the environment that we can use for implied bounds.
- ///
- /// Only used for Chalk.
- TypeWellFormedFromEnv(Ty<'tcx>),
-
/// A marker predicate that is always ambiguous.
/// Used for coherence to mark opaque types as possibly equal to each other but ambiguous.
Ambiguous,
- /// Separate from `Clause::Projection` which is used for normalization in new solver.
+ /// Separate from `ClauseKind::Projection` which is used for normalization in new solver.
/// This predicate requires two terms to be equal to eachother.
///
/// Only used for new solver
@@ -672,7 +741,7 @@ pub struct CratePredicatesMap<'tcx> {
pub predicates: FxHashMap<DefId, &'tcx [(Clause<'tcx>, Span)]>,
}
-impl<'tcx> Predicate<'tcx> {
+impl<'tcx> Clause<'tcx> {
/// Performs a substitution suitable for going from a
/// poly-trait-ref to supertraits that must hold if that
/// poly-trait-ref holds. This is slightly different from a normal
@@ -682,7 +751,7 @@ impl<'tcx> Predicate<'tcx> {
self,
tcx: TyCtxt<'tcx>,
trait_ref: &ty::PolyTraitRef<'tcx>,
- ) -> Predicate<'tcx> {
+ ) -> Clause<'tcx> {
// The interaction between HRTB and supertraits is not entirely
// obvious. Let me walk you (and myself) through an example.
//
@@ -764,11 +833,17 @@ impl<'tcx> Predicate<'tcx> {
let shifted_pred =
tcx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder());
// 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1>
- let new = EarlyBinder(shifted_pred).subst(tcx, trait_ref.skip_binder().substs);
+ let new = EarlyBinder::bind(shifted_pred).subst(tcx, trait_ref.skip_binder().substs);
// 3) ['x] + ['b] -> ['x, 'b]
let bound_vars =
tcx.mk_bound_variable_kinds_from_iter(trait_bound_vars.iter().chain(pred_bound_vars));
- tcx.reuse_or_mk_predicate(self, ty::Binder::bind_with_vars(new, bound_vars))
+
+ // FIXME: Is it really perf sensitive to use reuse_or_mk_predicate here?
+ tcx.reuse_or_mk_predicate(
+ self.as_predicate(),
+ ty::Binder::bind_with_vars(PredicateKind::Clause(new), bound_vars),
+ )
+ .expect_clause()
}
}
@@ -1189,13 +1264,41 @@ impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, PredicateKind<'tcx>> {
}
}
-impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for ClauseKind<'tcx> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(self)))
}
}
+impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, ClauseKind<'tcx>> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
+ tcx.mk_predicate(self.map_bound(ty::PredicateKind::Clause))
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
+ #[inline(always)]
+ fn to_predicate(self, _tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
+ self.as_predicate()
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ClauseKind<'tcx> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ tcx.mk_predicate(Binder::dummy(ty::PredicateKind::Clause(self))).expect_clause()
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for Binder<'tcx, ClauseKind<'tcx>> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ tcx.mk_predicate(self.map_bound(|clause| ty::PredicateKind::Clause(clause))).expect_clause()
+ }
+}
+
impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
@@ -1203,6 +1306,29 @@ impl<'tcx> ToPredicate<'tcx> for TraitRef<'tcx> {
}
}
+impl<'tcx> ToPredicate<'tcx, TraitPredicate<'tcx>> for TraitRef<'tcx> {
+ #[inline(always)]
+ fn to_predicate(self, _tcx: TyCtxt<'tcx>) -> TraitPredicate<'tcx> {
+ self.without_const()
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for TraitRef<'tcx> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ let p: Predicate<'tcx> = self.to_predicate(tcx);
+ p.expect_clause()
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for TraitPredicate<'tcx> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ let p: Predicate<'tcx> = self.to_predicate(tcx);
+ p.expect_clause()
+ }
+}
+
impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
#[inline(always)]
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
@@ -1211,6 +1337,14 @@ impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
}
}
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
+ #[inline(always)]
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ let pred: PolyTraitPredicate<'tcx> = self.to_predicate(tcx);
+ pred.to_predicate(tcx)
+ }
+}
+
impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
#[inline(always)]
fn to_predicate(self, _: TyCtxt<'tcx>) -> PolyTraitPredicate<'tcx> {
@@ -1236,31 +1370,45 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for TraitPredicate<'tcx>
impl<'tcx> ToPredicate<'tcx> for PolyTraitPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|p| PredicateKind::Clause(Clause::Trait(p))).to_predicate(tcx)
+ self.map_bound(|p| PredicateKind::Clause(ClauseKind::Trait(p))).to_predicate(tcx)
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyTraitPredicate<'tcx> {
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ let p: Predicate<'tcx> = self.to_predicate(tcx);
+ p.expect_clause()
}
}
impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|p| PredicateKind::Clause(Clause::RegionOutlives(p))).to_predicate(tcx)
+ self.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).to_predicate(tcx)
}
}
impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|p| PredicateKind::Clause(Clause::TypeOutlives(p))).to_predicate(tcx)
+ self.map_bound(|p| PredicateKind::Clause(ClauseKind::TypeOutlives(p))).to_predicate(tcx)
}
}
impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- self.map_bound(|p| PredicateKind::Clause(Clause::Projection(p))).to_predicate(tcx)
+ self.map_bound(|p| PredicateKind::Clause(ClauseKind::Projection(p))).to_predicate(tcx)
+ }
+}
+
+impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> {
+ fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> {
+ let p: Predicate<'tcx> = self.to_predicate(tcx);
+ p.expect_clause()
}
}
impl<'tcx> ToPredicate<'tcx> for TraitPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
- PredicateKind::Clause(Clause::Trait(self)).to_predicate(tcx)
+ PredicateKind::Clause(ClauseKind::Trait(self)).to_predicate(tcx)
}
}
@@ -1268,63 +1416,76 @@ impl<'tcx> Predicate<'tcx> {
pub fn to_opt_poly_trait_pred(self) -> Option<PolyTraitPredicate<'tcx>> {
let predicate = self.kind();
match predicate.skip_binder() {
- PredicateKind::Clause(Clause::Trait(t)) => Some(predicate.rebind(t)),
- PredicateKind::Clause(Clause::Projection(..))
- | PredicateKind::Clause(Clause::ConstArgHasType(..))
+ PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
+ PredicateKind::Clause(ClauseKind::Projection(..))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
- | PredicateKind::Clause(Clause::RegionOutlives(..))
- | PredicateKind::WellFormed(..)
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(..))
+ | PredicateKind::Clause(ClauseKind::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
- | PredicateKind::Clause(Clause::TypeOutlives(..))
- | PredicateKind::ConstEvaluatable(..)
+ | PredicateKind::Clause(ClauseKind::TypeOutlives(..))
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
- | PredicateKind::Ambiguous
- | PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | PredicateKind::Ambiguous => None,
}
}
pub fn to_opt_poly_projection_pred(self) -> Option<PolyProjectionPredicate<'tcx>> {
let predicate = self.kind();
match predicate.skip_binder() {
- PredicateKind::Clause(Clause::Projection(t)) => Some(predicate.rebind(t)),
- PredicateKind::Clause(Clause::Trait(..))
- | PredicateKind::Clause(Clause::ConstArgHasType(..))
+ PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)),
+ PredicateKind::Clause(ClauseKind::Trait(..))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
- | PredicateKind::Clause(Clause::RegionOutlives(..))
- | PredicateKind::WellFormed(..)
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(..))
+ | PredicateKind::Clause(ClauseKind::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
- | PredicateKind::Clause(Clause::TypeOutlives(..))
- | PredicateKind::ConstEvaluatable(..)
+ | PredicateKind::Clause(ClauseKind::TypeOutlives(..))
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
- | PredicateKind::Ambiguous
- | PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | PredicateKind::Ambiguous => None,
}
}
pub fn to_opt_type_outlives(self) -> Option<PolyTypeOutlivesPredicate<'tcx>> {
let predicate = self.kind();
match predicate.skip_binder() {
- PredicateKind::Clause(Clause::TypeOutlives(data)) => Some(predicate.rebind(data)),
- PredicateKind::Clause(Clause::Trait(..))
- | PredicateKind::Clause(Clause::ConstArgHasType(..))
- | PredicateKind::Clause(Clause::Projection(..))
+ PredicateKind::Clause(ClauseKind::TypeOutlives(data)) => Some(predicate.rebind(data)),
+ PredicateKind::Clause(ClauseKind::Trait(..))
+ | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
+ | PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
- | PredicateKind::Clause(Clause::RegionOutlives(..))
- | PredicateKind::WellFormed(..)
+ | PredicateKind::Clause(ClauseKind::RegionOutlives(..))
+ | PredicateKind::Clause(ClauseKind::WellFormed(..))
| PredicateKind::ObjectSafe(..)
| PredicateKind::ClosureKind(..)
- | PredicateKind::ConstEvaluatable(..)
+ | PredicateKind::Clause(ClauseKind::ConstEvaluatable(..))
| PredicateKind::ConstEquate(..)
- | PredicateKind::Ambiguous
- | PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | PredicateKind::Ambiguous => None,
+ }
+ }
+
+ /// Matches a `PredicateKind::Clause` and turns it into a `Clause`, otherwise returns `None`.
+ pub fn as_clause(self) -> Option<Clause<'tcx>> {
+ match self.kind().skip_binder() {
+ PredicateKind::Clause(..) => Some(self.expect_clause()),
+ _ => None,
+ }
+ }
+
+ /// Assert that the predicate is a clause.
+ pub fn expect_clause(self) -> Clause<'tcx> {
+ match self.kind().skip_binder() {
+ PredicateKind::Clause(..) => Clause(self.0),
+ _ => bug!("{self} is not a clause"),
}
}
}
@@ -1350,7 +1511,7 @@ impl<'tcx> Predicate<'tcx> {
/// [usize:Bar<isize>]]`.
#[derive(Clone, Debug, TypeFoldable, TypeVisitable)]
pub struct InstantiatedPredicates<'tcx> {
- pub predicates: Vec<Predicate<'tcx>>,
+ pub predicates: Vec<Clause<'tcx>>,
pub spans: Vec<Span>,
}
@@ -1369,9 +1530,9 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
}
impl<'tcx> IntoIterator for InstantiatedPredicates<'tcx> {
- type Item = (Predicate<'tcx>, Span);
+ type Item = (Clause<'tcx>, Span);
- type IntoIter = std::iter::Zip<std::vec::IntoIter<Predicate<'tcx>>, std::vec::IntoIter<Span>>;
+ type IntoIter = std::iter::Zip<std::vec::IntoIter<Clause<'tcx>>, std::vec::IntoIter<Span>>;
fn into_iter(self) -> Self::IntoIter {
debug_assert_eq!(self.predicates.len(), self.spans.len());
@@ -1380,10 +1541,10 @@ impl<'tcx> IntoIterator for InstantiatedPredicates<'tcx> {
}
impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> {
- type Item = (Predicate<'tcx>, Span);
+ type Item = (Clause<'tcx>, Span);
type IntoIter = std::iter::Zip<
- std::iter::Copied<std::slice::Iter<'a, Predicate<'tcx>>>,
+ std::iter::Copied<std::slice::Iter<'a, Clause<'tcx>>>,
std::iter::Copied<std::slice::Iter<'a, Span>>,
>;
@@ -1496,7 +1657,7 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
/// identified by both a universe, as well as a name residing within that universe. Distinct bound
/// regions/types/consts within the same universe simply have an unknown relationship to one
/// another.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(HashStable, TyEncodable, TyDecodable)]
pub struct Placeholder<T> {
pub universe: UniverseIndex,
@@ -1533,7 +1694,7 @@ pub struct ParamEnv<'tcx> {
/// want `Reveal::All`.
///
/// Note: This is packed, use the reveal() method to access it.
- packed: CopyTaggedPtr<&'tcx List<Predicate<'tcx>>, ParamTag, true>,
+ packed: CopyTaggedPtr<&'tcx List<Clause<'tcx>>, ParamTag, true>,
}
#[derive(Copy, Clone)]
@@ -1599,7 +1760,7 @@ impl<'tcx> ParamEnv<'tcx> {
}
#[inline]
- pub fn caller_bounds(self) -> &'tcx List<Predicate<'tcx>> {
+ pub fn caller_bounds(self) -> &'tcx List<Clause<'tcx>> {
self.packed.pointer()
}
@@ -1633,7 +1794,7 @@ impl<'tcx> ParamEnv<'tcx> {
/// Construct a trait environment with the given set of predicates.
#[inline]
pub fn new(
- caller_bounds: &'tcx List<Predicate<'tcx>>,
+ caller_bounds: &'tcx List<Clause<'tcx>>,
reveal: Reveal,
constness: hir::Constness,
) -> Self {
@@ -1874,6 +2035,22 @@ impl VariantDef {
&self.fields[FieldIdx::from_u32(0)]
}
+
+ /// Returns the last field in this variant, if present.
+ #[inline]
+ pub fn tail_opt(&self) -> Option<&FieldDef> {
+ self.fields.raw.last()
+ }
+
+ /// Returns the last field in this variant.
+ ///
+ /// # Panics
+ ///
+ /// Panics, if the variant has no fields.
+ #[inline]
+ pub fn tail(&self) -> &FieldDef {
+ self.tail_opt().expect("expected unsized ADT to have a tail field")
+ }
}
impl PartialEq for VariantDef {
diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs
index 1b336b7bf..b10921eff 100644
--- a/compiler/rustc_middle/src/ty/opaque_types.rs
+++ b/compiler/rustc_middle/src/ty/opaque_types.rs
@@ -141,7 +141,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
)
.emit();
- self.interner().mk_re_error(e)
+ ty::Region::new_error(self.interner(), e)
}
}
}
@@ -150,17 +150,17 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
match *ty.kind() {
ty::Closure(def_id, substs) => {
let substs = self.fold_closure_substs(def_id, substs);
- self.tcx.mk_closure(def_id, substs)
+ Ty::new_closure(self.tcx, def_id, substs)
}
ty::Generator(def_id, substs, movability) => {
let substs = self.fold_closure_substs(def_id, substs);
- self.tcx.mk_generator(def_id, substs, movability)
+ Ty::new_generator(self.tcx, def_id, substs, movability)
}
ty::GeneratorWitnessMIR(def_id, substs) => {
let substs = self.fold_closure_substs(def_id, substs);
- self.tcx.mk_generator_witness_mir(def_id, substs)
+ Ty::new_generator_witness_mir(self.tcx, def_id, substs)
}
ty::Param(param) => {
@@ -186,7 +186,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
.emit();
}
- self.interner().ty_error_misc()
+ Ty::new_misc_error(self.tcx)
}
}
}
@@ -216,7 +216,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
})
.emit_unless(self.ignore_errors);
- self.interner().const_error(ct.ty(), guar)
+ ty::Const::new_error(self.tcx, guar, ct.ty())
}
}
}
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index a2e77d9cd..cc2b26a5e 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -73,6 +73,7 @@ trivially_parameterized_over_tcx! {
ty::fast_reject::SimplifiedType,
rustc_ast::Attribute,
rustc_ast::DelimArgs,
+ rustc_ast::expand::StrippedCfgItem<rustc_hir::def_id::DefIndex>,
rustc_attr::ConstStability,
rustc_attr::DefaultBodyStability,
rustc_attr::Deprecation,
@@ -128,5 +129,6 @@ parameterized_over_tcx! {
ty::Const,
ty::Predicate,
ty::Clause,
+ ty::ClauseKind,
ty::GeneratorDiagnosticData,
}
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index 64e7480e6..2de0a3f75 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -123,7 +123,7 @@ pub trait Printer<'tcx>: Sized {
impl_trait_ref.map(|i| i.subst(self.tcx(), substs)),
)
} else {
- (self_ty.0, impl_trait_ref.map(|i| i.0))
+ (self_ty.subst_identity(), impl_trait_ref.map(|i| i.subst_identity()))
};
self.print_impl_path(def_id, substs, self_ty, impl_trait_ref)
}
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index d6c88ea96..96cf36eb9 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -94,6 +94,10 @@ macro_rules! define_helper {
$tl.with(|c| c.set(self.0))
}
}
+
+ pub fn $name() -> bool {
+ $tl.with(|c| c.get())
+ }
)+
}
}
@@ -184,7 +188,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
/// Convenience wrapper for `highlighting_region`.
pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) {
- self.highlighting_region(self.tcx.mk_re_var(vid), number)
+ self.highlighting_region(ty::Region::new_var(self.tcx, vid), number)
}
/// Returns `Some(n)` with the number to use for the given region, if any.
@@ -676,7 +680,7 @@ pub trait PrettyPrinter<'tcx>:
p!(")")
}
ty::FnDef(def_id, substs) => {
- if NO_QUERIES.with(|q| q.get()) {
+ if with_no_queries() {
p!(print_def_path(def_id, substs));
} else {
let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
@@ -685,29 +689,30 @@ pub trait PrettyPrinter<'tcx>:
}
ty::FnPtr(ref bare_fn) => p!(print(bare_fn)),
ty::Infer(infer_ty) => {
- let verbose = self.should_print_verbose();
+ if self.should_print_verbose() {
+ p!(write("{:?}", ty.kind()));
+ return Ok(self);
+ }
+
if let ty::TyVar(ty_vid) = infer_ty {
if let Some(name) = self.ty_infer_name(ty_vid) {
p!(write("{}", name))
} else {
- if verbose {
- p!(write("{:?}", infer_ty))
- } else {
- p!(write("{}", infer_ty))
- }
+ p!(write("{}", infer_ty))
}
} else {
- if verbose { p!(write("{:?}", infer_ty)) } else { p!(write("{}", infer_ty)) }
+ p!(write("{}", infer_ty))
}
}
ty::Error(_) => p!("{{type error}}"),
ty::Param(ref param_ty) => p!(print(param_ty)),
ty::Bound(debruijn, bound_ty) => match bound_ty.kind {
- ty::BoundTyKind::Anon => debug_bound_var(&mut self, debruijn, bound_ty.var)?,
+ ty::BoundTyKind::Anon => {
+ rustc_type_ir::debug_bound_var(&mut self, debruijn, bound_ty.var)?
+ }
ty::BoundTyKind::Param(_, s) => match self.should_print_verbose() {
- true if debruijn == ty::INNERMOST => p!(write("^{}", s)),
- true => p!(write("^{}_{}", debruijn.index(), s)),
- false => p!(write("{}", s)),
+ true => p!(write("{:?}", ty.kind())),
+ false => p!(write("{s}")),
},
},
ty::Adt(def, substs) => {
@@ -730,8 +735,8 @@ pub trait PrettyPrinter<'tcx>:
ty::Foreign(def_id) => {
p!(print_def_path(def_id, &[]));
}
- ty::Alias(ty::Projection | ty::Inherent, ref data) => {
- if !(self.should_print_verbose() || NO_QUERIES.with(|q| q.get()))
+ ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ref data) => {
+ if !(self.should_print_verbose() || with_no_queries())
&& self.tcx().is_impl_trait_in_trait(data.def_id)
{
return self.pretty_print_opaque_impl_type(data.def_id, data.substs);
@@ -740,10 +745,11 @@ pub trait PrettyPrinter<'tcx>:
}
}
ty::Placeholder(placeholder) => match placeholder.bound.kind {
- ty::BoundTyKind::Anon => {
- debug_placeholder_var(&mut self, placeholder.universe, placeholder.bound.var)?;
- }
- ty::BoundTyKind::Param(_, name) => p!(write("{}", name)),
+ ty::BoundTyKind::Anon => p!(write("{placeholder:?}")),
+ ty::BoundTyKind::Param(_, name) => match self.should_print_verbose() {
+ true => p!(write("{:?}", ty.kind())),
+ false => p!(write("{name}")),
+ },
},
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
// We use verbose printing in 'NO_QUERIES' mode, to
@@ -777,7 +783,7 @@ pub trait PrettyPrinter<'tcx>:
return Ok(self);
}
_ => {
- if NO_QUERIES.with(|q| q.get()) {
+ if with_no_queries() {
p!(print_def_path(def_id, &[]));
return Ok(self);
} else {
@@ -926,7 +932,7 @@ pub trait PrettyPrinter<'tcx>:
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::ClauseKind::Trait(pred) => {
let trait_ref = bound_predicate.rebind(pred.trait_ref);
// Don't print + Sized, but rather + ?Sized if absent.
@@ -937,7 +943,7 @@ pub trait PrettyPrinter<'tcx>:
self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
}
- ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => {
+ ty::ClauseKind::Projection(pred) => {
let proj_ref = bound_predicate.rebind(pred);
let trait_ref = proj_ref.required_poly_trait_ref(tcx);
@@ -951,7 +957,7 @@ pub trait PrettyPrinter<'tcx>:
&mut fn_traits,
);
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(outlives)) => {
+ ty::ClauseKind::TypeOutlives(outlives) => {
lifetimes.push(outlives.1);
}
_ => {}
@@ -1222,7 +1228,7 @@ pub trait PrettyPrinter<'tcx>:
// in order to place the projections inside the `<...>`.
if !resugared {
// Use a type that can't appear in defaults of type parameters.
- let dummy_cx = cx.tcx().mk_fresh_ty(0);
+ let dummy_cx = Ty::new_fresh(cx.tcx(), 0);
let principal = principal.with_self_ty(cx.tcx(), dummy_cx);
let args = cx
@@ -1372,11 +1378,9 @@ pub trait PrettyPrinter<'tcx>:
}
ty::ConstKind::Bound(debruijn, bound_var) => {
- debug_bound_var(&mut self, debruijn, bound_var)?
+ rustc_type_ir::debug_bound_var(&mut self, debruijn, bound_var)?
}
- ty::ConstKind::Placeholder(placeholder) => {
- debug_placeholder_var(&mut self, placeholder.universe, placeholder.bound)?;
- },
+ ty::ConstKind::Placeholder(placeholder) => p!(write("{placeholder:?}")),
// FIXME(generic_const_exprs):
// write out some legible representation of an abstract const?
ty::ConstKind::Expr(_) => p!("{{const expr}}"),
@@ -1389,11 +1393,12 @@ pub trait PrettyPrinter<'tcx>:
self,
scalar: Scalar,
ty: Ty<'tcx>,
- print_ty: bool,
) -> Result<Self::Const, Self::Error> {
match scalar {
- Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty, print_ty),
- Scalar::Int(int) => self.pretty_print_const_scalar_int(int, ty, print_ty),
+ Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty),
+ Scalar::Int(int) => {
+ self.pretty_print_const_scalar_int(int, ty, /* print_ty */ true)
+ }
}
}
@@ -1401,7 +1406,6 @@ pub trait PrettyPrinter<'tcx>:
mut self,
ptr: Pointer,
ty: Ty<'tcx>,
- print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);
@@ -1455,7 +1459,7 @@ pub trait PrettyPrinter<'tcx>:
_ => {}
}
// Any pointer values not covered by a branch above
- self = self.pretty_print_const_pointer(ptr, ty, print_ty)?;
+ self = self.pretty_print_const_pointer(ptr, ty)?;
Ok(self)
}
@@ -1523,24 +1527,18 @@ pub trait PrettyPrinter<'tcx>:
/// This is overridden for MIR printing because we only want to hide alloc ids from users, not
/// from MIR where it is actually useful.
fn pretty_print_const_pointer<Prov: Provenance>(
- mut self,
+ self,
_: Pointer<Prov>,
ty: Ty<'tcx>,
- print_ty: bool,
) -> Result<Self::Const, Self::Error> {
- if print_ty {
- self.typed_value(
- |mut this| {
- this.write_str("&_")?;
- Ok(this)
- },
- |this| this.print_type(ty),
- ": ",
- )
- } else {
- self.write_str("&_")?;
- Ok(self)
- }
+ self.typed_value(
+ |mut this| {
+ this.write_str("&_")?;
+ Ok(this)
+ },
+ |this| this.print_type(ty),
+ ": ",
+ )
}
fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self::Const, Self::Error> {
@@ -1597,7 +1595,8 @@ pub trait PrettyPrinter<'tcx>:
}
// Aggregates, printed as array/tuple/struct/variant construction syntax.
(ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
- let contents = self.tcx().destructure_const(self.tcx().mk_const(valtree, ty));
+ let contents =
+ self.tcx().destructure_const(ty::Const::new_value(self.tcx(), valtree, ty));
let fields = contents.fields.iter().copied();
match *ty.kind() {
ty::Array(..) => {
@@ -1746,7 +1745,8 @@ impl DerefMut for FmtPrinter<'_, '_> {
impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, ns: Namespace) -> Self {
- Self::new_with_limit(tcx, ns, tcx.type_length_limit())
+ let limit = if with_no_queries() { Limit::new(1048576) } else { tcx.type_length_limit() };
+ Self::new_with_limit(tcx, ns, limit)
}
pub fn new_with_limit(tcx: TyCtxt<'tcx>, ns: Namespace, type_length_limit: Limit) -> Self {
@@ -2150,7 +2150,6 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
self,
p: Pointer<Prov>,
ty: Ty<'tcx>,
- print_ty: bool,
) -> Result<Self::Const, Self::Error> {
let print = |mut this: Self| {
define_scoped_cx!(this);
@@ -2161,11 +2160,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
}
Ok(this)
};
- if print_ty {
- self.typed_value(print, |this| this.print_type(ty), ": ")
- } else {
- print(self)
- }
+ self.typed_value(print, |this| this.print_type(ty), ": ")
}
}
@@ -2303,7 +2298,7 @@ impl<'a, 'tcx> ty::TypeFolder<TyCtxt<'tcx>> for RegionFolder<'a, 'tcx> {
};
if let ty::ReLateBound(debruijn1, br) = *region {
assert_eq!(debruijn1, ty::INNERMOST);
- self.tcx.mk_re_late_bound(self.current_index, br)
+ ty::Region::new_late_bound(self.tcx, self.current_index, br)
} else {
region
}
@@ -2415,7 +2410,8 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
if let Some(lt_idx) = lifetime_idx {
if lt_idx > binder_level_idx {
let kind = ty::BrNamed(CRATE_DEF_ID.to_def_id(), name);
- return tcx.mk_re_late_bound(
+ return ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: br.var, kind },
);
@@ -2430,7 +2426,8 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
if let Some(lt_idx) = lifetime_idx {
if lt_idx > binder_level_idx {
let kind = ty::BrNamed(def_id, name);
- return tcx.mk_re_late_bound(
+ return ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: br.var, kind },
);
@@ -2443,7 +2440,8 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
if let Some(lt_idx) = lifetime_idx {
if lt_idx > binder_level_idx {
let kind = br.kind;
- return tcx.mk_re_late_bound(
+ return ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion { var: br.var, kind },
);
@@ -2458,7 +2456,11 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
start_or_continue(&mut self, "for<", ", ");
do_continue(&mut self, name);
}
- tcx.mk_re_late_bound(ty::INNERMOST, ty::BoundRegion { var: br.var, kind })
+ ty::Region::new_late_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundRegion { var: br.var, kind },
+ )
};
let mut folder = RegionFolder {
tcx,
@@ -2734,7 +2736,7 @@ define_print_and_forward_display! {
ty::ExistentialTraitRef<'tcx> {
// Use a type that can't appear in defaults of type parameters.
- let dummy_self = cx.tcx().mk_fresh_ty(0);
+ let dummy_self = Ty::new_fresh(cx.tcx(),0);
let trait_ref = self.with_self_ty(cx.tcx(), dummy_self);
p!(print(trait_ref.print_only_trait_path()))
}
@@ -2857,20 +2859,35 @@ define_print_and_forward_display! {
p!(print(binder))
}
+ ty::Clause<'tcx> {
+ p!(print(self.kind()))
+ }
+
+ ty::ClauseKind<'tcx> {
+ match *self {
+ ty::ClauseKind::Trait(ref data) => {
+ p!(print(data))
+ }
+ ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)),
+ ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)),
+ ty::ClauseKind::Projection(predicate) => p!(print(predicate)),
+ ty::ClauseKind::ConstArgHasType(ct, ty) => {
+ p!("the constant `", print(ct), "` has type `", print(ty), "`")
+ },
+ ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"),
+ ty::ClauseKind::ConstEvaluatable(ct) => {
+ p!("the constant `", print(ct), "` can be evaluated")
+ }
+ }
+ }
+
ty::PredicateKind<'tcx> {
match *self {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => {
+ ty::PredicateKind::Clause(data) => {
p!(print(data))
}
ty::PredicateKind::Subtype(predicate) => p!(print(predicate)),
ty::PredicateKind::Coerce(predicate) => p!(print(predicate)),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => p!(print(predicate)),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => p!(print(predicate)),
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => p!(print(predicate)),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
- p!("the constant `", print(ct), "` has type `", print(ty), "`")
- },
- ty::PredicateKind::WellFormed(arg) => p!(print(arg), " well-formed"),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe")
}
@@ -2879,15 +2896,9 @@ define_print_and_forward_display! {
print_value_path(closure_def_id, &[]),
write("` implements the trait `{}`", kind)
),
- ty::PredicateKind::ConstEvaluatable(ct) => {
- p!("the constant `", print(ct), "` can be evaluated")
- }
ty::PredicateKind::ConstEquate(c1, c2) => {
p!("the constant `", print(c1), "` equals `", print(c2), "`")
}
- ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
- p!("the type `", print(ty), "` is found in the environment")
- }
ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
}
@@ -2980,7 +2991,7 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
///
/// The implementation uses similar import discovery logic to that of 'use' suggestions.
///
-/// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths`].
+/// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`].
fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
let mut map: FxHashMap<DefId, Symbol> = FxHashMap::default();
@@ -3065,27 +3076,3 @@ pub struct OpaqueFnEntry<'tcx> {
fn_trait_ref: Option<ty::PolyTraitRef<'tcx>>,
return_ty: Option<ty::Binder<'tcx, Term<'tcx>>>,
}
-
-pub fn debug_bound_var<T: std::fmt::Write>(
- fmt: &mut T,
- debruijn: ty::DebruijnIndex,
- var: ty::BoundVar,
-) -> Result<(), std::fmt::Error> {
- if debruijn == ty::INNERMOST {
- write!(fmt, "^{}", var.index())
- } else {
- write!(fmt, "^{}_{}", debruijn.index(), var.index())
- }
-}
-
-pub fn debug_placeholder_var<T: std::fmt::Write>(
- fmt: &mut T,
- universe: ty::UniverseIndex,
- bound: ty::BoundVar,
-) -> Result<(), std::fmt::Error> {
- if universe == ty::UniverseIndex::ROOT {
- write!(fmt, "!{}", bound.index())
- } else {
- write!(fmt, "!{}_{}", universe.index(), bound.index())
- }
-}
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 3bbe6a23b..5741832c9 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -391,13 +391,13 @@ impl<'tcx> Relate<'tcx> for Ty<'tcx> {
/// Relates `a` and `b` structurally, calling the relation for all nested values.
/// Any semantic equality, e.g. of projections, and inference variables have to be
/// handled by the caller.
+#[instrument(level = "trace", skip(relation), ret)]
pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
relation: &mut R,
a: Ty<'tcx>,
b: Ty<'tcx>,
) -> RelateResult<'tcx, Ty<'tcx>> {
let tcx = relation.tcx();
- debug!("structurally_relate_tys: a={:?} b={:?}", a, b);
match (a.kind(), b.kind()) {
(&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
// The caller should handle these cases!
@@ -408,7 +408,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
bug!("bound types encountered in structurally_relate_tys")
}
- (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(tcx.ty_error(guar)),
+ (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(tcx, guar)),
(&ty::Never, _)
| (&ty::Char, _)
@@ -428,10 +428,10 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) if a_def == b_def => {
let substs = relation.relate_item_substs(a_def.did(), a_substs, b_substs)?;
- Ok(tcx.mk_adt(a_def, substs))
+ Ok(Ty::new_adt(tcx, a_def, substs))
}
- (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(tcx.mk_foreign(a_id)),
+ (&ty::Foreign(a_id), &ty::Foreign(b_id)) if a_id == b_id => Ok(Ty::new_foreign(tcx, a_id)),
(&ty::Dynamic(a_obj, a_region, a_repr), &ty::Dynamic(b_obj, b_region, b_repr))
if a_repr == b_repr =>
@@ -439,7 +439,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| {
relation.relate(a_region, b_region)
})?;
- Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound, a_repr))
+ Ok(Ty::new_dynamic(tcx, relation.relate(a_obj, b_obj)?, region_bound, a_repr))
}
(&ty::Generator(a_id, a_substs, movability), &ty::Generator(b_id, b_substs, _))
@@ -449,7 +449,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
// the (anonymous) type of the same generator expression. So
// all of their regions should be equated.
let substs = relation.relate(a_substs, b_substs)?;
- Ok(tcx.mk_generator(a_id, substs, movability))
+ Ok(Ty::new_generator(tcx, a_id, substs, movability))
}
(&ty::GeneratorWitness(a_types), &ty::GeneratorWitness(b_types)) => {
@@ -459,7 +459,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
let b_types = b_types.map_bound(GeneratorWitness);
// Then remove the GeneratorWitness for the result
let types = relation.relate(a_types, b_types)?.map_bound(|witness| witness.0);
- Ok(tcx.mk_generator_witness(types))
+ Ok(Ty::new_generator_witness(tcx, types))
}
(&ty::GeneratorWitnessMIR(a_id, a_substs), &ty::GeneratorWitnessMIR(b_id, b_substs))
@@ -469,7 +469,7 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
// the (anonymous) type of the same generator expression. So
// all of their regions should be equated.
let substs = relation.relate(a_substs, b_substs)?;
- Ok(tcx.mk_generator_witness_mir(a_id, substs))
+ Ok(Ty::new_generator_witness_mir(tcx, a_id, substs))
}
(&ty::Closure(a_id, a_substs), &ty::Closure(b_id, b_substs)) if a_id == b_id => {
@@ -477,12 +477,12 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
// the (anonymous) type of the same closure expression. So
// all of their regions should be equated.
let substs = relation.relate(a_substs, b_substs)?;
- Ok(tcx.mk_closure(a_id, &substs))
+ Ok(Ty::new_closure(tcx, a_id, &substs))
}
(&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => {
let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
- Ok(tcx.mk_ptr(mt))
+ Ok(Ty::new_ptr(tcx, mt))
}
(&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
@@ -490,13 +490,13 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl };
let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl };
let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
- Ok(tcx.mk_ref(r, mt))
+ Ok(Ty::new_ref(tcx, r, mt))
}
(&ty::Array(a_t, sz_a), &ty::Array(b_t, sz_b)) => {
let t = relation.relate(a_t, b_t)?;
match relation.relate(sz_a, sz_b) {
- Ok(sz) => Ok(tcx.mk_array_with_const_len(t, sz)),
+ Ok(sz) => Ok(Ty::new_array_with_const_len(tcx, t, sz)),
Err(err) => {
// Check whether the lengths are both concrete/known values,
// but are unequal, for better diagnostics.
@@ -519,12 +519,15 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
(&ty::Slice(a_t), &ty::Slice(b_t)) => {
let t = relation.relate(a_t, b_t)?;
- Ok(tcx.mk_slice(t))
+ Ok(Ty::new_slice(tcx, t))
}
(&ty::Tuple(as_), &ty::Tuple(bs)) => {
if as_.len() == bs.len() {
- Ok(tcx.mk_tup_from_iter(iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)))?)
+ Ok(Ty::new_tup_from_iter(
+ tcx,
+ iter::zip(as_, bs).map(|(a, b)| relation.relate(a, b)),
+ )?)
} else if !(as_.is_empty() || bs.is_empty()) {
Err(TypeError::TupleSize(expected_found(relation, as_.len(), bs.len())))
} else {
@@ -536,25 +539,16 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
if a_def_id == b_def_id =>
{
let substs = relation.relate_item_substs(a_def_id, a_substs, b_substs)?;
- Ok(tcx.mk_fn_def(a_def_id, substs))
+ Ok(Ty::new_fn_def(tcx, a_def_id, substs))
}
(&ty::FnPtr(a_fty), &ty::FnPtr(b_fty)) => {
let fty = relation.relate(a_fty, b_fty)?;
- Ok(tcx.mk_fn_ptr(fty))
- }
-
- // these two are already handled downstream in case of lazy normalization
- (&ty::Alias(ty::Projection, a_data), &ty::Alias(ty::Projection, b_data)) => {
- let projection_ty = relation.relate(a_data, b_data)?;
- Ok(tcx.mk_projection(projection_ty.def_id, projection_ty.substs))
- }
-
- (&ty::Alias(ty::Inherent, a_data), &ty::Alias(ty::Inherent, b_data)) => {
- let alias_ty = relation.relate(a_data, b_data)?;
- Ok(tcx.mk_alias(ty::Inherent, tcx.mk_alias_ty(alias_ty.def_id, alias_ty.substs)))
+ Ok(Ty::new_fn_ptr(tcx, fty))
}
+ // The substs of opaque types may not all be invariant, so we have
+ // to treat them separately from other aliases.
(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, substs: a_substs, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, substs: b_substs, .. }),
@@ -568,7 +562,20 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
b_substs,
false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
)?;
- Ok(tcx.mk_opaque(a_def_id, substs))
+ Ok(Ty::new_opaque(tcx, a_def_id, substs))
+ }
+
+ // Alias tend to mostly already be handled downstream due to normalization.
+ (&ty::Alias(a_kind, a_data), &ty::Alias(b_kind, b_data)) => {
+ // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): This if can be removed
+ // and the assert uncommented once the new desugaring is stable.
+ if a_kind == b_kind {
+ let alias_ty = relation.relate(a_data, b_data)?;
+ // assert_eq!(a_kind, b_kind);
+ Ok(Ty::new_alias(tcx, a_kind, alias_ty))
+ } else {
+ Err(TypeError::Sorts(expected_found(relation, a, b)))
+ }
}
_ => Err(TypeError::Sorts(expected_found(relation, a, b))),
@@ -589,17 +596,6 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
debug!("{}.structurally_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
let tcx = relation.tcx();
- // HACK(const_generics): We still need to eagerly evaluate consts when
- // relating them because during `normalize_param_env_or_error`,
- // we may relate an evaluated constant in a obligation against
- // an unnormalized (i.e. unevaluated) const in the param-env.
- // FIXME(generic_const_exprs): Once we always lazily unify unevaluated constants
- // these `eval` calls can be removed.
- if !tcx.features().generic_const_exprs {
- a = a.eval(tcx, relation.param_env());
- b = b.eval(tcx, relation.param_env());
- }
-
if tcx.features().generic_const_exprs {
a = tcx.expand_abstract_consts(a);
b = tcx.expand_abstract_consts(b);
@@ -634,7 +630,11 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
au.substs,
bu.substs,
)?;
- return Ok(tcx.mk_const(ty::UnevaluatedConst { def: au.def, substs }, a.ty()));
+ return Ok(ty::Const::new_unevaluated(
+ tcx,
+ ty::UnevaluatedConst { def: au.def, substs },
+ a.ty(),
+ ));
}
// Before calling relate on exprs, it is necessary to ensure that the nested consts
// have identical types.
@@ -675,8 +675,7 @@ pub fn structurally_relate_consts<'tcx, R: TypeRelation<'tcx>>(
}
_ => return Err(TypeError::ConstMismatch(expected_found(r, a, b))),
};
- let kind = ty::ConstKind::Expr(expr);
- return Ok(tcx.mk_const(kind, a.ty()));
+ return Ok(ty::Const::new_expr(tcx, expr, a.ty()));
}
_ => false,
};
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 16cb6c910..7220d133f 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -11,6 +11,7 @@ use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
use rustc_hir::def::Namespace;
use rustc_index::{Idx, IndexVec};
use rustc_target::abi::TyAndLayout;
+use rustc_type_ir::ConstKind;
use std::fmt;
use std::ops::ControlFlow;
@@ -88,7 +89,35 @@ impl fmt::Debug for ty::FreeRegion {
impl<'tcx> fmt::Debug for ty::FnSig<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "({:?}; c_variadic: {})->{:?}", self.inputs(), self.c_variadic, self.output())
+ let ty::FnSig { inputs_and_output: _, c_variadic, unsafety, abi } = self;
+
+ write!(f, "{}", unsafety.prefix_str())?;
+ match abi {
+ rustc_target::spec::abi::Abi::Rust => (),
+ abi => write!(f, "extern \"{abi:?}\" ")?,
+ };
+
+ write!(f, "fn(")?;
+ let inputs = self.inputs();
+ match inputs.len() {
+ 0 if *c_variadic => write!(f, "...)")?,
+ 0 => write!(f, ")")?,
+ _ => {
+ for ty in &self.inputs()[0..(self.inputs().len() - 1)] {
+ write!(f, "{ty:?}, ")?;
+ }
+ write!(f, "{:?}", self.inputs().last().unwrap())?;
+ if *c_variadic {
+ write!(f, "...")?;
+ }
+ write!(f, ")")?;
+ }
+ }
+
+ match self.output().kind() {
+ ty::Tuple(list) if list.is_empty() => Ok(()),
+ _ => write!(f, " -> {:?}", self.output()),
+ }
}
}
@@ -145,12 +174,22 @@ impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
impl<'tcx> fmt::Debug for ty::Clause<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?}", self.kind())
+ }
+}
+
+impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
- ty::Clause::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"),
- ty::Clause::Trait(ref a) => a.fmt(f),
- ty::Clause::RegionOutlives(ref pair) => pair.fmt(f),
- ty::Clause::TypeOutlives(ref pair) => pair.fmt(f),
- ty::Clause::Projection(ref pair) => pair.fmt(f),
+ ty::ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"),
+ ty::ClauseKind::Trait(ref a) => a.fmt(f),
+ ty::ClauseKind::RegionOutlives(ref pair) => pair.fmt(f),
+ ty::ClauseKind::TypeOutlives(ref pair) => pair.fmt(f),
+ ty::ClauseKind::Projection(ref pair) => pair.fmt(f),
+ ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({:?})", data),
+ ty::ClauseKind::ConstEvaluatable(ct) => {
+ write!(f, "ConstEvaluatable({ct:?})")
+ }
}
}
}
@@ -161,20 +200,13 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
ty::PredicateKind::Clause(ref a) => a.fmt(f),
ty::PredicateKind::Subtype(ref pair) => pair.fmt(f),
ty::PredicateKind::Coerce(ref pair) => pair.fmt(f),
- ty::PredicateKind::WellFormed(data) => write!(f, "WellFormed({:?})", data),
ty::PredicateKind::ObjectSafe(trait_def_id) => {
write!(f, "ObjectSafe({:?})", trait_def_id)
}
ty::PredicateKind::ClosureKind(closure_def_id, closure_substs, kind) => {
write!(f, "ClosureKind({:?}, {:?}, {:?})", closure_def_id, closure_substs, kind)
}
- ty::PredicateKind::ConstEvaluatable(ct) => {
- write!(f, "ConstEvaluatable({ct:?})")
- }
ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({:?}, {:?})", c1, c2),
- ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
- write!(f, "TypeWellFormedFromEnv({:?})", ty)
- }
ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"),
ty::PredicateKind::AliasRelate(t1, t2, dir) => {
write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})")
@@ -210,22 +242,21 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
}
}
-impl<'tcx> fmt::Debug for ty::ConstKind<'tcx> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- use ty::ConstKind::*;
- match self {
- Param(param) => write!(f, "{param:?}"),
- Infer(var) => write!(f, "{var:?}"),
- Bound(debruijn, var) => ty::print::debug_bound_var(f, *debruijn, *var),
- Placeholder(placeholder) => {
- ty::print::debug_placeholder_var(f, placeholder.universe, placeholder.bound)
- }
- Unevaluated(uv) => {
- f.debug_tuple("Unevaluated").field(&uv.substs).field(&uv.def).finish()
- }
- Value(valtree) => write!(f, "{valtree:?}"),
- Error(_) => write!(f, "[const error]"),
- Expr(expr) => write!(f, "{expr:?}"),
+impl fmt::Debug for ty::BoundTy {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.kind {
+ ty::BoundTyKind::Anon => write!(f, "{:?}", self.var),
+ ty::BoundTyKind::Param(_, sym) => write!(f, "{sym:?}"),
+ }
+ }
+}
+
+impl<T: fmt::Debug> fmt::Debug for ty::Placeholder<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if self.universe == ty::UniverseIndex::ROOT {
+ write!(f, "!{:?}", self.bound)
+ } else {
+ write!(f, "!{}_{:?}", self.universe.index(), self.bound)
}
}
}
@@ -294,13 +325,14 @@ TrivialTypeTraversalAndLiftImpls! {
crate::ty::AliasRelationDirection,
crate::ty::Placeholder<crate::ty::BoundRegion>,
crate::ty::Placeholder<crate::ty::BoundTy>,
+ crate::ty::Placeholder<ty::BoundVar>,
crate::ty::ClosureKind,
crate::ty::FreeRegion,
crate::ty::InferTy,
crate::ty::IntVarValue,
crate::ty::ParamConst,
crate::ty::ParamTy,
- crate::ty::adjustment::PointerCast,
+ crate::ty::adjustment::PointerCoercion,
crate::ty::RegionVid,
crate::ty::UniverseIndex,
crate::ty::Variance,
@@ -310,7 +342,6 @@ TrivialTypeTraversalAndLiftImpls! {
interpret::Scalar,
rustc_target::abi::Size,
ty::BoundVar,
- ty::Placeholder<ty::BoundVar>,
}
TrivialTypeTraversalAndLiftImpls! {
@@ -609,12 +640,28 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
}
}
+// FIXME(clause): This is wonky
+impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ty::Clause<'tcx> {
+ fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(folder.try_fold_predicate(self.as_predicate())?.expect_clause())
+ }
+}
+
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
visitor.visit_predicate(*self)
}
}
+impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ty::Clause<'tcx> {
+ fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ visitor.visit_predicate(self.as_predicate())
+ }
+}
+
impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
fn try_super_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
self,
@@ -634,12 +681,12 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
}
}
-impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ty::Predicate<'tcx>> {
+impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for &'tcx ty::List<ty::Clause<'tcx>> {
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
- ty::util::fold_list(self, folder, |tcx, v| tcx.mk_predicates(v))
+ ty::util::fold_list(self, folder, |tcx, v| tcx.mk_clauses(v))
}
}
@@ -664,9 +711,20 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
folder: &mut F,
) -> Result<Self, F::Error> {
let ty = self.ty().try_fold_with(folder)?;
- let kind = self.kind().try_fold_with(folder)?;
+ let kind = match self.kind() {
+ ConstKind::Param(p) => ConstKind::Param(p.try_fold_with(folder)?),
+ ConstKind::Infer(i) => ConstKind::Infer(i.try_fold_with(folder)?),
+ ConstKind::Bound(d, b) => {
+ ConstKind::Bound(d.try_fold_with(folder)?, b.try_fold_with(folder)?)
+ }
+ ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
+ ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
+ ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
+ ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
+ ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
+ };
if ty != self.ty() || kind != self.kind() {
- Ok(folder.interner().mk_const(kind, ty))
+ Ok(folder.interner().mk_ct_from_kind(kind, ty))
} else {
Ok(self)
}
@@ -679,7 +737,19 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
visitor: &mut V,
) -> ControlFlow<V::BreakTy> {
self.ty().visit_with(visitor)?;
- self.kind().visit_with(visitor)
+ match self.kind() {
+ ConstKind::Param(p) => p.visit_with(visitor),
+ ConstKind::Infer(i) => i.visit_with(visitor),
+ ConstKind::Bound(d, b) => {
+ d.visit_with(visitor)?;
+ b.visit_with(visitor)
+ }
+ ConstKind::Placeholder(p) => p.visit_with(visitor),
+ ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
+ ConstKind::Value(v) => v.visit_with(visitor),
+ ConstKind::Error(e) => e.visit_with(visitor),
+ ConstKind::Expr(e) => e.visit_with(visitor),
+ }
}
}
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index e6d51c4ec..94746fbdc 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -15,16 +15,18 @@ use hir::def::DefKind;
use polonius_engine::Atom;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::intern::Interned;
-use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
+use rustc_error_messages::DiagnosticMessage;
+use rustc_errors::{DiagnosticArgValue, ErrorGuaranteed, IntoDiagnosticArg, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::LangItem;
use rustc_index::Idx;
use rustc_macros::HashStable;
use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
use rustc_target::spec::abi::{self, Abi};
+use std::assert_matches::debug_assert_matches;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt;
@@ -33,13 +35,17 @@ use std::ops::{ControlFlow, Deref, Range};
use ty::util::IntTypeExt;
use rustc_type_ir::sty::TyKind::*;
-use rustc_type_ir::RegionKind as IrRegionKind;
use rustc_type_ir::TyKind as IrTyKind;
+use rustc_type_ir::{CollectAndApply, ConstKind as IrConstKind};
+use rustc_type_ir::{DynKind, RegionKind as IrRegionKind};
+
+use super::GenericParamDefKind;
// Re-export the `TyKind` from `rustc_type_ir` here for convenience
#[rustc_diagnostic_item = "TyKind"]
pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
+pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
@@ -568,7 +574,7 @@ impl<'tcx> GeneratorSubsts<'tcx> {
let layout = tcx.generator_layout(def_id).unwrap();
layout.variant_fields.iter().map(move |variant| {
variant.iter().map(move |field| {
- ty::EarlyBinder(layout.field_tys[*field].ty).subst(tcx, self.substs)
+ ty::EarlyBinder::bind(layout.field_tys[*field].ty).subst(tcx, self.substs)
})
})
}
@@ -715,7 +721,7 @@ impl<'tcx> PolyExistentialPredicate<'tcx> {
/// Given an existential predicate like `?Self: PartialEq<u32>` (e.g., derived from `dyn PartialEq<u32>`),
/// and a concrete type `self_ty`, returns a full predicate where the existentially quantified variable `?Self`
/// has been replaced with `self_ty` (e.g., `self_ty: PartialEq<u32>`, in our example).
- pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Predicate<'tcx> {
+ pub fn with_self_ty(&self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ty::Clause<'tcx> {
use crate::ty::ToPredicate;
match self.skip_binder() {
ExistentialPredicate::Trait(tr) => {
@@ -1007,7 +1013,10 @@ impl BoundVariableKind {
/// `Decodable` and `Encodable` are implemented for `Binder<T>` using the `impl_binder_encode_decode!` macro.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(HashStable, Lift)]
-pub struct Binder<'tcx, T>(T, &'tcx List<BoundVariableKind>);
+pub struct Binder<'tcx, T> {
+ value: T,
+ bound_vars: &'tcx List<BoundVariableKind>,
+}
impl<'tcx, T> Binder<'tcx, T>
where
@@ -1023,15 +1032,15 @@ where
!value.has_escaping_bound_vars(),
"`{value:?}` has escaping bound vars, so it cannot be wrapped in a dummy binder."
);
- Binder(value, ty::List::empty())
+ Binder { value, bound_vars: ty::List::empty() }
}
- pub fn bind_with_vars(value: T, vars: &'tcx List<BoundVariableKind>) -> Binder<'tcx, T> {
+ pub fn bind_with_vars(value: T, bound_vars: &'tcx List<BoundVariableKind>) -> Binder<'tcx, T> {
if cfg!(debug_assertions) {
- let mut validator = ValidateBoundVars::new(vars);
+ let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
- Binder(value, vars)
+ Binder { value, bound_vars }
}
}
@@ -1053,30 +1062,30 @@ impl<'tcx, T> Binder<'tcx, T> {
/// - comparing the self type of a PolyTraitRef to see if it is equal to
/// a type parameter `X`, since the type `X` does not reference any regions
pub fn skip_binder(self) -> T {
- self.0
+ self.value
}
pub fn bound_vars(&self) -> &'tcx List<BoundVariableKind> {
- self.1
+ self.bound_vars
}
pub fn as_ref(&self) -> Binder<'tcx, &T> {
- Binder(&self.0, self.1)
+ Binder { value: &self.value, bound_vars: self.bound_vars }
}
pub fn as_deref(&self) -> Binder<'tcx, &T::Target>
where
T: Deref,
{
- Binder(&self.0, self.1)
+ Binder { value: &self.value, bound_vars: self.bound_vars }
}
pub fn map_bound_ref_unchecked<F, U>(&self, f: F) -> Binder<'tcx, U>
where
F: FnOnce(&T) -> U,
{
- let value = f(&self.0);
- Binder(value, self.1)
+ let value = f(&self.value);
+ Binder { value, bound_vars: self.bound_vars }
}
pub fn map_bound_ref<F, U: TypeVisitable<TyCtxt<'tcx>>>(&self, f: F) -> Binder<'tcx, U>
@@ -1090,12 +1099,13 @@ impl<'tcx, T> Binder<'tcx, T> {
where
F: FnOnce(T) -> U,
{
- let value = f(self.0);
+ let Binder { value, bound_vars } = self;
+ let value = f(value);
if cfg!(debug_assertions) {
- let mut validator = ValidateBoundVars::new(self.1);
+ let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
- Binder(value, self.1)
+ Binder { value, bound_vars }
}
pub fn try_map_bound<F, U: TypeVisitable<TyCtxt<'tcx>>, E>(
@@ -1105,12 +1115,13 @@ impl<'tcx, T> Binder<'tcx, T> {
where
F: FnOnce(T) -> Result<U, E>,
{
- let value = f(self.0)?;
+ let Binder { value, bound_vars } = self;
+ let value = f(value)?;
if cfg!(debug_assertions) {
- let mut validator = ValidateBoundVars::new(self.1);
+ let mut validator = ValidateBoundVars::new(bound_vars);
value.visit_with(&mut validator);
}
- Ok(Binder(value, self.1))
+ Ok(Binder { value, bound_vars })
}
/// Wraps a `value` in a binder, using the same bound variables as the
@@ -1126,11 +1137,7 @@ impl<'tcx, T> Binder<'tcx, T> {
where
U: TypeVisitable<TyCtxt<'tcx>>,
{
- if cfg!(debug_assertions) {
- let mut validator = ValidateBoundVars::new(self.bound_vars());
- value.visit_with(&mut validator);
- }
- Binder(value, self.1)
+ Binder::bind_with_vars(value, self.bound_vars)
}
/// Unwraps and returns the value within, but only if it contains
@@ -1147,7 +1154,7 @@ impl<'tcx, T> Binder<'tcx, T> {
where
T: TypeVisitable<TyCtxt<'tcx>>,
{
- if self.0.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
+ if self.value.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
}
/// Splits the contents into two things that share the same binder
@@ -1160,22 +1167,23 @@ impl<'tcx, T> Binder<'tcx, T> {
where
F: FnOnce(T) -> (U, V),
{
- let (u, v) = f(self.0);
- (Binder(u, self.1), Binder(v, self.1))
+ let Binder { value, bound_vars } = self;
+ let (u, v) = f(value);
+ (Binder { value: u, bound_vars }, Binder { value: v, bound_vars })
}
}
impl<'tcx, T> Binder<'tcx, Option<T>> {
pub fn transpose(self) -> Option<Binder<'tcx, T>> {
- let bound_vars = self.1;
- self.0.map(|v| Binder(v, bound_vars))
+ let Binder { value, bound_vars } = self;
+ value.map(|value| Binder { value, bound_vars })
}
}
impl<'tcx, T: IntoIterator> Binder<'tcx, T> {
pub fn iter(self) -> impl Iterator<Item = ty::Binder<'tcx, T::Item>> {
- let bound_vars = self.1;
- self.0.into_iter().map(|v| Binder(v, bound_vars))
+ let Binder { value, bound_vars } = self;
+ value.into_iter().map(|value| Binder { value, bound_vars })
}
}
@@ -1184,7 +1192,7 @@ where
T: IntoDiagnosticArg,
{
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
- self.0.into_diagnostic_arg()
+ self.value.into_diagnostic_arg()
}
}
@@ -1231,12 +1239,13 @@ impl<'tcx> AliasTy<'tcx> {
DefKind::AssocTy if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(self.def_id)) => ty::Inherent,
DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection,
DefKind::OpaqueTy => ty::Opaque,
+ DefKind::TyAlias => ty::Weak,
kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
}
}
pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
- tcx.mk_alias(self.kind(tcx), self)
+ Ty::new_alias(tcx, self.kind(tcx), self)
}
}
@@ -1427,7 +1436,7 @@ impl<'tcx> ParamTy {
#[inline]
pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
- tcx.mk_ty_param(self.index, self.name)
+ Ty::new_param(tcx, self.index, self.name)
}
pub fn span_from_generics(&self, tcx: TyCtxt<'tcx>, item_with_generics: DefId) -> Span {
@@ -1459,6 +1468,103 @@ impl ParamConst {
#[rustc_pass_by_value]
pub struct Region<'tcx>(pub Interned<'tcx, RegionKind<'tcx>>);
+impl<'tcx> Region<'tcx> {
+ #[inline]
+ pub fn new_early_bound(
+ tcx: TyCtxt<'tcx>,
+ early_bound_region: ty::EarlyBoundRegion,
+ ) -> Region<'tcx> {
+ tcx.intern_region(ty::ReEarlyBound(early_bound_region))
+ }
+
+ #[inline]
+ pub fn new_late_bound(
+ tcx: TyCtxt<'tcx>,
+ debruijn: ty::DebruijnIndex,
+ bound_region: ty::BoundRegion,
+ ) -> Region<'tcx> {
+ // Use a pre-interned one when possible.
+ if let ty::BoundRegion { var, kind: ty::BrAnon(None) } = bound_region
+ && let Some(inner) = tcx.lifetimes.re_late_bounds.get(debruijn.as_usize())
+ && let Some(re) = inner.get(var.as_usize()).copied()
+ {
+ re
+ } else {
+ tcx.intern_region(ty::ReLateBound(debruijn, bound_region))
+ }
+ }
+
+ #[inline]
+ pub fn new_free(
+ tcx: TyCtxt<'tcx>,
+ scope: DefId,
+ bound_region: ty::BoundRegionKind,
+ ) -> Region<'tcx> {
+ tcx.intern_region(ty::ReFree(ty::FreeRegion { scope, bound_region }))
+ }
+
+ #[inline]
+ pub fn new_var(tcx: TyCtxt<'tcx>, v: ty::RegionVid) -> Region<'tcx> {
+ // Use a pre-interned one when possible.
+ tcx.lifetimes
+ .re_vars
+ .get(v.as_usize())
+ .copied()
+ .unwrap_or_else(|| tcx.intern_region(ty::ReVar(v)))
+ }
+
+ #[inline]
+ pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderRegion) -> Region<'tcx> {
+ tcx.intern_region(ty::RePlaceholder(placeholder))
+ }
+
+ /// Constructs a `RegionKind::ReError` region.
+ #[track_caller]
+ pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Region<'tcx> {
+ tcx.intern_region(ty::ReError(reported))
+ }
+
+ /// Constructs a `RegionKind::ReError` region and registers a `delay_span_bug` to ensure it
+ /// gets used.
+ #[track_caller]
+ pub fn new_error_misc(tcx: TyCtxt<'tcx>) -> Region<'tcx> {
+ Region::new_error_with_message(
+ tcx,
+ DUMMY_SP,
+ "RegionKind::ReError constructed but no error reported",
+ )
+ }
+
+ /// Constructs a `RegionKind::ReError` region and registers a `delay_span_bug` with the given
+ /// `msg` to ensure it gets used.
+ #[track_caller]
+ pub fn new_error_with_message<S: Into<MultiSpan>>(
+ tcx: TyCtxt<'tcx>,
+ span: S,
+ msg: &'static str,
+ ) -> Region<'tcx> {
+ let reported = tcx.sess.delay_span_bug(span, msg);
+ Region::new_error(tcx, reported)
+ }
+
+ /// Avoid this in favour of more specific `new_*` methods, where possible,
+ /// to avoid the cost of the `match`.
+ pub fn new_from_kind(tcx: TyCtxt<'tcx>, kind: RegionKind<'tcx>) -> Region<'tcx> {
+ match kind {
+ ty::ReEarlyBound(region) => Region::new_early_bound(tcx, region),
+ ty::ReLateBound(debruijn, region) => Region::new_late_bound(tcx, debruijn, region),
+ ty::ReFree(ty::FreeRegion { scope, bound_region }) => {
+ Region::new_free(tcx, scope, bound_region)
+ }
+ ty::ReStatic => tcx.lifetimes.re_static,
+ ty::ReVar(vid) => Region::new_var(tcx, vid),
+ ty::RePlaceholder(region) => Region::new_placeholder(tcx, region),
+ ty::ReErased => tcx.lifetimes.re_erased,
+ ty::ReError(reported) => Region::new_error(tcx, reported),
+ }
+ }
+}
+
impl<'tcx> Deref for Region<'tcx> {
type Target = RegionKind<'tcx>;
@@ -1484,7 +1590,7 @@ pub struct EarlyBoundRegion {
impl fmt::Debug for EarlyBoundRegion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}, {}", self.index, self.name)
+ write!(f, "{:?}, {}, {}", self.def_id, self.index, self.name)
}
}
@@ -1511,10 +1617,11 @@ impl Atom for RegionVid {
rustc_index::newtype_index! {
#[derive(HashStable)]
+ #[debug_format = "{}"]
pub struct BoundVar {}
}
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub struct BoundTy {
pub var: BoundVar,
@@ -1770,6 +1877,390 @@ impl<'tcx> Region<'tcx> {
}
}
+/// Constructors for `Ty`
+impl<'tcx> Ty<'tcx> {
+ // Avoid this in favour of more specific `new_*` methods, where possible.
+ #[allow(rustc::usage_of_ty_tykind)]
+ #[inline]
+ pub fn new(tcx: TyCtxt<'tcx>, st: TyKind<'tcx>) -> Ty<'tcx> {
+ tcx.mk_ty_from_kind(st)
+ }
+
+ #[inline]
+ pub fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Ty<'tcx> {
+ Ty::new(tcx, TyKind::Infer(infer))
+ }
+
+ #[inline]
+ pub fn new_var(tcx: TyCtxt<'tcx>, v: ty::TyVid) -> Ty<'tcx> {
+ // Use a pre-interned one when possible.
+ tcx.types
+ .ty_vars
+ .get(v.as_usize())
+ .copied()
+ .unwrap_or_else(|| Ty::new(tcx, Infer(TyVar(v))))
+ }
+
+ #[inline]
+ pub fn new_int_var(tcx: TyCtxt<'tcx>, v: ty::IntVid) -> Ty<'tcx> {
+ Ty::new_infer(tcx, IntVar(v))
+ }
+
+ #[inline]
+ pub fn new_float_var(tcx: TyCtxt<'tcx>, v: ty::FloatVid) -> Ty<'tcx> {
+ Ty::new_infer(tcx, FloatVar(v))
+ }
+
+ #[inline]
+ pub fn new_fresh(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+ // Use a pre-interned one when possible.
+ tcx.types
+ .fresh_tys
+ .get(n as usize)
+ .copied()
+ .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshTy(n)))
+ }
+
+ #[inline]
+ pub fn new_fresh_int(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+ // Use a pre-interned one when possible.
+ tcx.types
+ .fresh_int_tys
+ .get(n as usize)
+ .copied()
+ .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshIntTy(n)))
+ }
+
+ #[inline]
+ pub fn new_fresh_float(tcx: TyCtxt<'tcx>, n: u32) -> Ty<'tcx> {
+ // Use a pre-interned one when possible.
+ tcx.types
+ .fresh_float_tys
+ .get(n as usize)
+ .copied()
+ .unwrap_or_else(|| Ty::new_infer(tcx, ty::FreshFloatTy(n)))
+ }
+
+ #[inline]
+ pub fn new_param(tcx: TyCtxt<'tcx>, index: u32, name: Symbol) -> Ty<'tcx> {
+ tcx.mk_ty_from_kind(Param(ParamTy { index, name }))
+ }
+
+ #[inline]
+ pub fn new_bound(
+ tcx: TyCtxt<'tcx>,
+ index: ty::DebruijnIndex,
+ bound_ty: ty::BoundTy,
+ ) -> Ty<'tcx> {
+ Ty::new(tcx, Bound(index, bound_ty))
+ }
+
+ #[inline]
+ pub fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType) -> Ty<'tcx> {
+ Ty::new(tcx, Placeholder(placeholder))
+ }
+
+ #[inline]
+ pub fn new_alias(
+ tcx: TyCtxt<'tcx>,
+ kind: ty::AliasKind,
+ alias_ty: ty::AliasTy<'tcx>,
+ ) -> Ty<'tcx> {
+ debug_assert_matches!(
+ (kind, tcx.def_kind(alias_ty.def_id)),
+ (ty::Opaque, DefKind::OpaqueTy)
+ | (ty::Projection | ty::Inherent, DefKind::AssocTy)
+ | (ty::Opaque | ty::Projection, DefKind::ImplTraitPlaceholder)
+ | (ty::Weak, DefKind::TyAlias)
+ );
+ Ty::new(tcx, Alias(kind, alias_ty))
+ }
+
+ #[inline]
+ pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
+ Ty::new_alias(tcx, ty::Opaque, tcx.mk_alias_ty(def_id, substs))
+ }
+
+ /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
+ pub fn new_error(tcx: TyCtxt<'tcx>, reported: ErrorGuaranteed) -> Ty<'tcx> {
+ Ty::new(tcx, Error(reported))
+ }
+
+ /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
+ #[track_caller]
+ pub fn new_misc_error(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ Ty::new_error_with_message(tcx, DUMMY_SP, "TyKind::Error constructed but no error reported")
+ }
+
+ /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` with the given `msg` to
+ /// ensure it gets used.
+ #[track_caller]
+ pub fn new_error_with_message<S: Into<MultiSpan>>(
+ tcx: TyCtxt<'tcx>,
+ span: S,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> Ty<'tcx> {
+ let reported = tcx.sess.delay_span_bug(span, msg);
+ Ty::new(tcx, Error(reported))
+ }
+
+ #[inline]
+ pub fn new_int(tcx: TyCtxt<'tcx>, i: ty::IntTy) -> Ty<'tcx> {
+ use ty::IntTy::*;
+ match i {
+ Isize => tcx.types.isize,
+ I8 => tcx.types.i8,
+ I16 => tcx.types.i16,
+ I32 => tcx.types.i32,
+ I64 => tcx.types.i64,
+ I128 => tcx.types.i128,
+ }
+ }
+
+ #[inline]
+ pub fn new_uint(tcx: TyCtxt<'tcx>, ui: ty::UintTy) -> Ty<'tcx> {
+ use ty::UintTy::*;
+ match ui {
+ Usize => tcx.types.usize,
+ U8 => tcx.types.u8,
+ U16 => tcx.types.u16,
+ U32 => tcx.types.u32,
+ U64 => tcx.types.u64,
+ U128 => tcx.types.u128,
+ }
+ }
+
+ #[inline]
+ pub fn new_float(tcx: TyCtxt<'tcx>, f: ty::FloatTy) -> Ty<'tcx> {
+ use ty::FloatTy::*;
+ match f {
+ F32 => tcx.types.f32,
+ F64 => tcx.types.f64,
+ }
+ }
+
+ #[inline]
+ pub fn new_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
+ Ty::new(tcx, Ref(r, tm.ty, tm.mutbl))
+ }
+
+ #[inline]
+ pub fn new_mut_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
+ }
+
+ #[inline]
+ pub fn new_imm_ref(tcx: TyCtxt<'tcx>, r: Region<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ Ty::new_ref(tcx, r, TypeAndMut { ty, mutbl: hir::Mutability::Not })
+ }
+
+ #[inline]
+ pub fn new_ptr(tcx: TyCtxt<'tcx>, tm: TypeAndMut<'tcx>) -> Ty<'tcx> {
+ Ty::new(tcx, RawPtr(tm))
+ }
+
+ #[inline]
+ pub fn new_mut_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Mut })
+ }
+
+ #[inline]
+ pub fn new_imm_ptr(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: hir::Mutability::Not })
+ }
+
+ #[inline]
+ pub fn new_adt(tcx: TyCtxt<'tcx>, def: AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
+ Ty::new(tcx, Adt(def, substs))
+ }
+
+ #[inline]
+ pub fn new_foreign(tcx: TyCtxt<'tcx>, def_id: DefId) -> Ty<'tcx> {
+ Ty::new(tcx, Foreign(def_id))
+ }
+
+ #[inline]
+ pub fn new_array(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, n: u64) -> Ty<'tcx> {
+ Ty::new(tcx, Array(ty, ty::Const::from_target_usize(tcx, n)))
+ }
+
+ #[inline]
+ pub fn new_array_with_const_len(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ ct: ty::Const<'tcx>,
+ ) -> Ty<'tcx> {
+ Ty::new(tcx, Array(ty, ct))
+ }
+
+ #[inline]
+ pub fn new_slice(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ Ty::new(tcx, Slice(ty))
+ }
+
+ #[inline]
+ pub fn new_tup(tcx: TyCtxt<'tcx>, ts: &[Ty<'tcx>]) -> Ty<'tcx> {
+ if ts.is_empty() { tcx.types.unit } else { Ty::new(tcx, Tuple(tcx.mk_type_list(&ts))) }
+ }
+
+ pub fn new_tup_from_iter<I, T>(tcx: TyCtxt<'tcx>, iter: I) -> T::Output
+ where
+ I: Iterator<Item = T>,
+ T: CollectAndApply<Ty<'tcx>, Ty<'tcx>>,
+ {
+ T::collect_and_apply(iter, |ts| Ty::new_tup(tcx, ts))
+ }
+
+ #[inline]
+ pub fn new_fn_def(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+ ) -> Ty<'tcx> {
+ let substs = tcx.check_and_mk_substs(def_id, substs);
+ Ty::new(tcx, FnDef(def_id, substs))
+ }
+
+ #[inline]
+ pub fn new_fn_ptr(tcx: TyCtxt<'tcx>, fty: PolyFnSig<'tcx>) -> Ty<'tcx> {
+ Ty::new(tcx, FnPtr(fty))
+ }
+
+ #[inline]
+ pub fn new_dynamic(
+ tcx: TyCtxt<'tcx>,
+ obj: &'tcx List<PolyExistentialPredicate<'tcx>>,
+ reg: ty::Region<'tcx>,
+ repr: DynKind,
+ ) -> Ty<'tcx> {
+ Ty::new(tcx, Dynamic(obj, reg, repr))
+ }
+
+ #[inline]
+ pub fn new_projection(
+ tcx: TyCtxt<'tcx>,
+ item_def_id: DefId,
+ substs: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
+ ) -> Ty<'tcx> {
+ Ty::new_alias(tcx, ty::Projection, tcx.mk_alias_ty(item_def_id, substs))
+ }
+
+ #[inline]
+ pub fn new_closure(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ closure_substs: SubstsRef<'tcx>,
+ ) -> Ty<'tcx> {
+ debug_assert_eq!(
+ closure_substs.len(),
+ tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 3,
+ "closure constructed with incorrect substitutions"
+ );
+ Ty::new(tcx, Closure(def_id, closure_substs))
+ }
+
+ #[inline]
+ pub fn new_generator(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ generator_substs: SubstsRef<'tcx>,
+ movability: hir::Movability,
+ ) -> Ty<'tcx> {
+ debug_assert_eq!(
+ generator_substs.len(),
+ tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
+ "generator constructed with incorrect number of substitutions"
+ );
+ Ty::new(tcx, Generator(def_id, generator_substs, movability))
+ }
+
+ #[inline]
+ pub fn new_generator_witness(
+ tcx: TyCtxt<'tcx>,
+ types: ty::Binder<'tcx, &'tcx List<Ty<'tcx>>>,
+ ) -> Ty<'tcx> {
+ Ty::new(tcx, GeneratorWitness(types))
+ }
+
+ #[inline]
+ pub fn new_generator_witness_mir(
+ tcx: TyCtxt<'tcx>,
+ id: DefId,
+ substs: SubstsRef<'tcx>,
+ ) -> Ty<'tcx> {
+ Ty::new(tcx, GeneratorWitnessMIR(id, substs))
+ }
+
+ // misc
+
+ #[inline]
+ pub fn new_unit(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ tcx.types.unit
+ }
+
+ #[inline]
+ pub fn new_static_str(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_)
+ }
+
+ #[inline]
+ pub fn new_diverging_default(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ if tcx.features().never_type_fallback { tcx.types.never } else { tcx.types.unit }
+ }
+
+ // lang and diagnostic tys
+
+ fn new_generic_adt(tcx: TyCtxt<'tcx>, wrapper_def_id: DefId, ty_param: Ty<'tcx>) -> Ty<'tcx> {
+ let adt_def = tcx.adt_def(wrapper_def_id);
+ let substs =
+ InternalSubsts::for_item(tcx, wrapper_def_id, |param, substs| match param.kind {
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => bug!(),
+ GenericParamDefKind::Type { has_default, .. } => {
+ if param.index == 0 {
+ ty_param.into()
+ } else {
+ assert!(has_default);
+ tcx.type_of(param.def_id).subst(tcx, substs).into()
+ }
+ }
+ });
+ Ty::new(tcx, Adt(adt_def, substs))
+ }
+
+ #[inline]
+ pub fn new_lang_item(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, item: LangItem) -> Option<Ty<'tcx>> {
+ let def_id = tcx.lang_items().get(item)?;
+ Some(Ty::new_generic_adt(tcx, def_id, ty))
+ }
+
+ #[inline]
+ pub fn new_diagnostic_item(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> Option<Ty<'tcx>> {
+ let def_id = tcx.get_diagnostic_item(name)?;
+ Some(Ty::new_generic_adt(tcx, def_id, ty))
+ }
+
+ #[inline]
+ pub fn new_box(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let def_id = tcx.require_lang_item(LangItem::OwnedBox, None);
+ Ty::new_generic_adt(tcx, def_id, ty)
+ }
+
+ #[inline]
+ pub fn new_maybe_uninit(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let def_id = tcx.require_lang_item(LangItem::MaybeUninit, None);
+ Ty::new_generic_adt(tcx, def_id, ty)
+ }
+
+ /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
+ pub fn new_task_context(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ let context_did = tcx.require_lang_item(LangItem::Context, None);
+ let context_adt_ref = tcx.adt_def(context_did);
+ let context_substs = tcx.mk_substs(&[tcx.lifetimes.re_erased.into()]);
+ let context_ty = Ty::new_adt(tcx, context_adt_ref, context_substs);
+ Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, context_ty)
+ }
+}
+
/// Type utilities
impl<'tcx> Ty<'tcx> {
#[inline(always)]
@@ -2213,7 +2704,7 @@ impl<'tcx> Ty<'tcx> {
let assoc_items = tcx.associated_item_def_ids(
tcx.require_lang_item(hir::LangItem::DiscriminantKind, None),
);
- tcx.mk_projection(assoc_items[0], tcx.mk_substs(&[self.into()]))
+ Ty::new_projection(tcx, assoc_items[0], tcx.mk_substs(&[self.into()]))
}
ty::Bool
@@ -2366,7 +2857,7 @@ impl<'tcx> Ty<'tcx> {
ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
- ty::Adt(def, _substs) => def.sized_constraint(tcx).0.is_empty(),
+ ty::Adt(def, _substs) => def.sized_constraint(tcx).skip_binder().is_empty(),
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => false,
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 43f95635a..4d5f5b865 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -11,6 +11,7 @@ use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_serialize::{self, Decodable, Encodable};
+use rustc_span::sym;
use rustc_type_ir::WithCachedTypeInfo;
use smallvec::SmallVec;
@@ -451,6 +452,10 @@ impl<'tcx> InternalSubsts<'tcx> {
pub fn truncate_to(&self, tcx: TyCtxt<'tcx>, generics: &ty::Generics) -> SubstsRef<'tcx> {
tcx.mk_substs_from_iter(self.iter().take(generics.count()))
}
+
+ pub fn host_effect_param(&'tcx self) -> Option<ty::Const<'tcx>> {
+ self.consts().rfind(|x| matches!(x.kind(), ty::ConstKind::Param(p) if p.name == sym::host))
+ }
}
impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for SubstsRef<'tcx> {
@@ -538,15 +543,21 @@ impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitable<TyCtxt<'tcx>> for &'tcx
/// [`subst_identity`](EarlyBinder::subst_identity) or [`skip_binder`](EarlyBinder::skip_binder).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Encodable, Decodable, HashStable)]
-pub struct EarlyBinder<T>(pub T);
+pub struct EarlyBinder<T> {
+ value: T,
+}
/// For early binders, you should first call `subst` before using any visitors.
impl<'tcx, T> !TypeFoldable<TyCtxt<'tcx>> for ty::EarlyBinder<T> {}
impl<'tcx, T> !TypeVisitable<TyCtxt<'tcx>> for ty::EarlyBinder<T> {}
impl<T> EarlyBinder<T> {
+ pub fn bind(value: T) -> EarlyBinder<T> {
+ EarlyBinder { value }
+ }
+
pub fn as_ref(&self) -> EarlyBinder<&T> {
- EarlyBinder(&self.0)
+ EarlyBinder { value: &self.value }
}
pub fn map_bound_ref<F, U>(&self, f: F) -> EarlyBinder<U>
@@ -560,20 +571,20 @@ impl<T> EarlyBinder<T> {
where
F: FnOnce(T) -> U,
{
- let value = f(self.0);
- EarlyBinder(value)
+ let value = f(self.value);
+ EarlyBinder { value }
}
pub fn try_map_bound<F, U, E>(self, f: F) -> Result<EarlyBinder<U>, E>
where
F: FnOnce(T) -> Result<U, E>,
{
- let value = f(self.0)?;
- Ok(EarlyBinder(value))
+ let value = f(self.value)?;
+ Ok(EarlyBinder { value })
}
pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> {
- EarlyBinder(value)
+ EarlyBinder { value }
}
/// Skips the binder and returns the "bound" value.
@@ -582,22 +593,26 @@ impl<T> EarlyBinder<T> {
/// arguments of an `FnSig`). Otherwise, consider using
/// [`subst_identity`](EarlyBinder::subst_identity).
///
+ /// To skip the binder on `x: &EarlyBinder<T>` to obtain `&T`, leverage
+ /// [`EarlyBinder::as_ref`](EarlyBinder::as_ref): `x.as_ref().skip_binder()`.
+ ///
/// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
/// the analogous operation on [`super::Binder`].
pub fn skip_binder(self) -> T {
- self.0
+ self.value
}
}
impl<T> EarlyBinder<Option<T>> {
pub fn transpose(self) -> Option<EarlyBinder<T>> {
- self.0.map(|v| EarlyBinder(v))
+ self.value.map(|value| EarlyBinder { value })
}
}
impl<T, U> EarlyBinder<(T, U)> {
pub fn transpose_tuple2(self) -> (EarlyBinder<T>, EarlyBinder<U>) {
- (EarlyBinder(self.0.0), EarlyBinder(self.0.1))
+ let EarlyBinder { value: (lhs, rhs) } = self;
+ (EarlyBinder { value: lhs }, EarlyBinder { value: rhs })
}
}
@@ -610,13 +625,13 @@ where
tcx: TyCtxt<'tcx>,
substs: &'s [GenericArg<'tcx>],
) -> SubstIter<'s, 'tcx, I> {
- SubstIter { it: self.0.into_iter(), tcx, substs }
+ SubstIter { it: self.value.into_iter(), tcx, substs }
}
/// Similar to [`subst_identity`](EarlyBinder::subst_identity),
/// but on an iterator of `TypeFoldable` values.
pub fn subst_identity_iter(self) -> I::IntoIter {
- self.0.into_iter()
+ self.value.into_iter()
}
}
@@ -633,7 +648,7 @@ where
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
- Some(EarlyBinder(self.it.next()?).subst(self.tcx, self.substs))
+ Some(EarlyBinder { value: self.it.next()? }.subst(self.tcx, self.substs))
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -647,7 +662,7 @@ where
I::Item: TypeFoldable<TyCtxt<'tcx>>,
{
fn next_back(&mut self) -> Option<Self::Item> {
- Some(EarlyBinder(self.it.next_back()?).subst(self.tcx, self.substs))
+ Some(EarlyBinder { value: self.it.next_back()? }.subst(self.tcx, self.substs))
}
}
@@ -668,13 +683,13 @@ where
tcx: TyCtxt<'tcx>,
substs: &'s [GenericArg<'tcx>],
) -> SubstIterCopied<'s, 'tcx, I> {
- SubstIterCopied { it: self.0.into_iter(), tcx, substs }
+ SubstIterCopied { it: self.value.into_iter(), tcx, substs }
}
/// Similar to [`subst_identity`](EarlyBinder::subst_identity),
/// but on an iterator of values that deref to a `TypeFoldable`.
pub fn subst_identity_iter_copied(self) -> impl Iterator<Item = <I::Item as Deref>::Target> {
- self.0.into_iter().map(|v| *v)
+ self.value.into_iter().map(|v| *v)
}
}
@@ -692,7 +707,7 @@ where
type Item = <I::Item as Deref>::Target;
fn next(&mut self) -> Option<Self::Item> {
- Some(EarlyBinder(*self.it.next()?).subst(self.tcx, self.substs))
+ self.it.next().map(|value| EarlyBinder { value: *value }.subst(self.tcx, self.substs))
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -707,7 +722,7 @@ where
<I::Item as Deref>::Target: Copy + TypeFoldable<TyCtxt<'tcx>>,
{
fn next_back(&mut self) -> Option<Self::Item> {
- Some(EarlyBinder(*self.it.next_back()?).subst(self.tcx, self.substs))
+ self.it.next_back().map(|value| EarlyBinder { value: *value }.subst(self.tcx, self.substs))
}
}
@@ -725,7 +740,7 @@ pub struct EarlyBinderIter<T> {
impl<T: IntoIterator> EarlyBinder<T> {
pub fn transpose_iter(self) -> EarlyBinderIter<T::IntoIter> {
- EarlyBinderIter { t: self.0.into_iter() }
+ EarlyBinderIter { t: self.value.into_iter() }
}
}
@@ -733,7 +748,7 @@ impl<T: Iterator> Iterator for EarlyBinderIter<T> {
type Item = EarlyBinder<T::Item>;
fn next(&mut self) -> Option<Self::Item> {
- self.t.next().map(|i| EarlyBinder(i))
+ self.t.next().map(|value| EarlyBinder { value })
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -744,7 +759,7 @@ impl<T: Iterator> Iterator for EarlyBinderIter<T> {
impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> ty::EarlyBinder<T> {
pub fn subst(self, tcx: TyCtxt<'tcx>, substs: &[GenericArg<'tcx>]) -> T {
let mut folder = SubstFolder { tcx, substs, binders_passed: 0 };
- self.0.fold_with(&mut folder)
+ self.value.fold_with(&mut folder)
}
/// Makes the identity substitution `T0 => T0, ..., TN => TN`.
@@ -756,12 +771,12 @@ impl<'tcx, T: TypeFoldable<TyCtxt<'tcx>>> ty::EarlyBinder<T> {
/// - Inside of the body of `foo`, we treat `T` as a placeholder by calling
/// `subst_identity` to discharge the `EarlyBinder`.
pub fn subst_identity(self) -> T {
- self.0
+ self.value
}
/// Returns the inner value, but only if it contains no bound vars.
pub fn no_bound_vars(self) -> Option<T> {
- if !self.0.has_param() { Some(self.0) } else { None }
+ if !self.value.has_param() { Some(self.value) } else { None }
}
}
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index e61037e5e..98c70e330 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -52,6 +52,16 @@ pub struct TraitDef {
/// List of functions from `#[rustc_must_implement_one_of]` attribute one of which
/// must be implemented.
pub must_implement_one_of: Option<Box<[Ident]>>,
+
+ /// Whether to add a builtin `dyn Trait: Trait` implementation.
+ /// This is enabled for all traits except ones marked with
+ /// `#[rustc_deny_explicit_impl(implement_via_object = false)]`.
+ pub implement_via_object: bool,
+
+ /// Whether a trait is fully built-in, and any implementation is disallowed.
+ /// This only applies to built-in traits, and is marked via
+ /// `#[rustc_deny_explicit_impl(implement_via_object = ...)]`.
+ pub deny_explicit_impl: bool,
}
/// Whether this trait is treated specially by the standard library
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index e04dbbff9..8cbffa148 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -155,11 +155,7 @@ pub struct TypeckResults<'tcx> {
/// We also store the type here, so that the compiler can use it as a hint
/// for figuring out hidden types, even if they are only set in dead code
/// (which doesn't show up in MIR).
- ///
- /// These types are mapped back to the opaque's identity substitutions
- /// (with erased regions), which is why we don't associated substs with any
- /// of these usages.
- pub concrete_opaque_types: FxIndexMap<LocalDefId, ty::OpaqueHiddenType<'tcx>>,
+ pub concrete_opaque_types: FxIndexMap<ty::OpaqueTypeKey<'tcx>, ty::OpaqueHiddenType<'tcx>>,
/// Tracks the minimum captures required for a closure;
/// see `MinCaptureInformationMap` for more details.
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index ba0513563..e2e4a2dbd 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1,7 +1,6 @@
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use crate::mir;
use crate::query::Providers;
use crate::ty::layout::IntegerExt;
use crate::ty::{
@@ -11,13 +10,12 @@ use crate::ty::{
use crate::ty::{GenericArgKind, SubstsRef};
use rustc_apfloat::Float as _;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
+use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_index::bit_set::GrowableBitSet;
-use rustc_index::{Idx, IndexVec};
use rustc_macros::HashStable;
use rustc_session::Limit;
use rustc_span::sym;
@@ -129,7 +127,7 @@ impl IntTypeExt for IntegerType {
impl<'tcx> TyCtxt<'tcx> {
/// Creates a hash of the type `Ty` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
- pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash64 {
+ pub fn type_id_hash(self, ty: Ty<'tcx>) -> Hash128 {
// We want the type_id be independent of the types free regions, so we
// erase them. The erase_regions() call will also anonymize bound
// regions, which is desirable too.
@@ -173,18 +171,6 @@ impl<'tcx> TyCtxt<'tcx> {
}
}
- pub fn has_error_field(self, ty: Ty<'tcx>) -> bool {
- if let ty::Adt(def, substs) = *ty.kind() {
- for field in def.all_fields() {
- let field_ty = field.ty(self, substs);
- if let ty::Error(_) = field_ty.kind() {
- return true;
- }
- }
- }
- false
- }
-
/// Attempts to returns the deeply last field of nested structures, but
/// does not apply any normalization in its search. Returns the same type
/// if input `ty` is not a structure at all.
@@ -237,14 +223,14 @@ impl<'tcx> TyCtxt<'tcx> {
};
let reported =
self.sess.emit_err(crate::error::RecursionLimitReached { ty, suggested_limit });
- return self.ty_error(reported);
+ return Ty::new_error(self, reported);
}
match *ty.kind() {
ty::Adt(def, substs) => {
if !def.is_struct() {
break;
}
- match def.non_enum_variant().fields.raw.last() {
+ match def.non_enum_variant().tail_opt() {
Some(field) => {
f();
ty = field.ty(self, substs);
@@ -318,7 +304,7 @@ impl<'tcx> TyCtxt<'tcx> {
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
if a_def == b_def && a_def.is_struct() =>
{
- if let Some(f) = a_def.non_enum_variant().fields.raw.last() {
+ if let Some(f) = a_def.non_enum_variant().tail_opt() {
a = f.ty(self, a_substs);
b = f.ty(self, b_substs);
} else {
@@ -624,12 +610,12 @@ impl<'tcx> TyCtxt<'tcx> {
closure_substs: SubstsRef<'tcx>,
env_region: ty::Region<'tcx>,
) -> Option<Ty<'tcx>> {
- let closure_ty = self.mk_closure(closure_def_id, closure_substs);
+ let closure_ty = Ty::new_closure(self, closure_def_id, closure_substs);
let closure_kind_ty = closure_substs.as_closure().kind_ty();
let closure_kind = closure_kind_ty.to_opt_closure_kind()?;
let env_ty = match closure_kind {
- ty::ClosureKind::Fn => self.mk_imm_ref(env_region, closure_ty),
- ty::ClosureKind::FnMut => self.mk_mut_ref(env_region, closure_ty),
+ ty::ClosureKind::Fn => Ty::new_imm_ref(self, env_region, closure_ty),
+ ty::ClosureKind::FnMut => Ty::new_mut_ref(self, env_region, closure_ty),
ty::ClosureKind::FnOnce => closure_ty,
};
Some(env_ty)
@@ -670,12 +656,12 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn thread_local_ptr_ty(self, def_id: DefId) -> Ty<'tcx> {
let static_ty = self.type_of(def_id).subst_identity();
if self.is_mutable_static(def_id) {
- self.mk_mut_ptr(static_ty)
+ Ty::new_mut_ptr(self, static_ty)
} else if self.is_foreign_item(def_id) {
- self.mk_imm_ptr(static_ty)
+ Ty::new_imm_ptr(self, static_ty)
} else {
// FIXME: These things don't *really* have 'static lifetime.
- self.mk_imm_ref(self.lifetimes.re_static, static_ty)
+ Ty::new_imm_ref(self, self.lifetimes.re_static, static_ty)
}
}
@@ -690,11 +676,11 @@ impl<'tcx> TyCtxt<'tcx> {
// 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)
+ Ty::new_mut_ptr(self, static_ty)
} else if self.is_foreign_item(def_id) {
- self.mk_imm_ptr(static_ty)
+ Ty::new_imm_ptr(self, static_ty)
} else {
- self.mk_imm_ref(self.lifetimes.re_erased, static_ty)
+ Ty::new_imm_ref(self, self.lifetimes.re_erased, static_ty)
}
}
@@ -709,7 +695,7 @@ impl<'tcx> TyCtxt<'tcx> {
.as_ref()
.map_or_else(|| [].iter(), |l| l.field_tys.iter())
.filter(|decl| !decl.ignore_for_traits)
- .map(|decl| ty::EarlyBinder(decl.ty))
+ .map(|decl| ty::EarlyBinder::bind(decl.ty))
}
/// Normalizes all opaque types in the given value, replacing them
@@ -750,80 +736,6 @@ impl<'tcx> TyCtxt<'tcx> {
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
- /// Returns names of captured upvars for closures and generators.
- ///
- /// Here are some examples:
- /// - `name__field1__field2` when the upvar is captured by value.
- /// - `_ref__name__field` when the upvar is captured by reference.
- ///
- /// For generators this only contains upvars that are shared by all states.
- pub fn closure_saved_names_of_captured_variables(
- self,
- def_id: DefId,
- ) -> SmallVec<[String; 16]> {
- let body = self.optimized_mir(def_id);
-
- body.var_debug_info
- .iter()
- .filter_map(|var| {
- let is_ref = match var.value {
- mir::VarDebugInfoContents::Place(place)
- if place.local == mir::Local::new(1) =>
- {
- // The projection is either `[.., Field, Deref]` or `[.., Field]`. It
- // implies whether the variable is captured by value or by reference.
- matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
- }
- _ => return None,
- };
- let prefix = if is_ref { "_ref__" } else { "" };
- Some(prefix.to_owned() + var.name.as_str())
- })
- .collect()
- }
-
- // FIXME(eddyb) maybe precompute this? Right now it's computed once
- // per generator monomorphization, but it doesn't depend on substs.
- pub fn generator_layout_and_saved_local_names(
- self,
- def_id: DefId,
- ) -> (
- &'tcx ty::GeneratorLayout<'tcx>,
- IndexVec<mir::GeneratorSavedLocal, Option<rustc_span::Symbol>>,
- ) {
- let tcx = self;
- let body = tcx.optimized_mir(def_id);
- let generator_layout = body.generator_layout().unwrap();
- let mut generator_saved_local_names =
- IndexVec::from_elem(None, &generator_layout.field_tys);
-
- let state_arg = mir::Local::new(1);
- for var in &body.var_debug_info {
- let mir::VarDebugInfoContents::Place(place) = &var.value else { continue };
- if place.local != state_arg {
- continue;
- }
- match place.projection[..] {
- [
- // Deref of the `Pin<&mut Self>` state argument.
- mir::ProjectionElem::Field(..),
- mir::ProjectionElem::Deref,
- // Field of a variant of the state.
- mir::ProjectionElem::Downcast(_, variant),
- mir::ProjectionElem::Field(field, _),
- ] => {
- let name = &mut generator_saved_local_names
- [generator_layout.variant_fields[variant][field]];
- if name.is_none() {
- name.replace(var.name);
- }
- }
- _ => {}
- }
- }
- (generator_layout, generator_saved_local_names)
- }
-
/// Query and get an English description for the item's kind.
pub fn def_descr(self, def_id: DefId) -> &'static str {
self.def_kind_descr(self.def_kind(def_id), def_id)
@@ -857,6 +769,26 @@ impl<'tcx> TyCtxt<'tcx> {
_ => def_kind.article(),
}
}
+
+ /// Return `true` if the supplied `CrateNum` is "user-visible," meaning either a [public]
+ /// dependency, or a [direct] private dependency. This is used to decide whether the crate can
+ /// be shown in `impl` suggestions.
+ ///
+ /// [public]: TyCtxt::is_private_dep
+ /// [direct]: rustc_session::cstore::ExternCrate::is_direct
+ pub fn is_user_visible_dep(self, key: CrateNum) -> bool {
+ // | Private | Direct | Visible | |
+ // |---------|--------|---------|--------------------|
+ // | Yes | Yes | Yes | !true || true |
+ // | No | Yes | Yes | !false || true |
+ // | Yes | No | No | !true || false |
+ // | No | No | Yes | !false || false |
+ !self.is_private_dep(key)
+ // If `extern_crate` is `None`, then the crate was injected (e.g., by the allocator).
+ // Treat that kind of crate as "indirect", since it's an implementation detail of
+ // the language.
+ || self.extern_crate(key.as_def_id()).map_or(false, |e| e.is_direct())
+ }
}
struct OpaqueTypeExpander<'tcx> {
@@ -922,7 +854,7 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
let hidden_ty = bty.subst(self.tcx, substs);
self.fold_ty(hidden_ty);
}
- let expanded_ty = self.tcx.mk_generator_witness_mir(def_id, substs);
+ let expanded_ty = Ty::new_generator_witness_mir(self.tcx, def_id, substs);
self.expanded_cache.insert((def_id, substs), expanded_ty);
expanded_ty
}
@@ -964,7 +896,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
if let ty::PredicateKind::Clause(clause) = p.kind().skip_binder()
- && let ty::Clause::Projection(projection_pred) = clause
+ && let ty::ClauseKind::Projection(projection_pred) = clause
{
p.kind()
.rebind(ty::ProjectionPredicate {
@@ -1374,7 +1306,7 @@ pub fn needs_drop_components<'tcx>(
ty::Array(elem_ty, size) => {
match needs_drop_components(*elem_ty, target_layout) {
Ok(v) if v.is_empty() => Ok(v),
- res => match size.kind().try_to_bits(target_layout.pointer_size) {
+ res => match size.try_to_bits(target_layout.pointer_size) {
// Arrays of size zero don't need drop, even if their element
// type does.
Some(0) => Ok(SmallVec::new()),
@@ -1487,8 +1419,8 @@ pub struct AlwaysRequiresDrop;
/// with their underlying types.
pub fn reveal_opaque_types_in_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
- val: &'tcx ty::List<ty::Predicate<'tcx>>,
-) -> &'tcx ty::List<ty::Predicate<'tcx>> {
+ val: &'tcx ty::List<ty::Clause<'tcx>>,
+) -> &'tcx ty::List<ty::Clause<'tcx>> {
let mut visitor = OpaqueTypeExpander {
seen_opaque_tys: FxHashSet::default(),
expanded_cache: FxHashMap::default(),
diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs
index b9b1cd73a..443791d0a 100644
--- a/compiler/rustc_middle/src/ty/vtable.rs
+++ b/compiler/rustc_middle/src/ty/vtable.rs
@@ -73,7 +73,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
let ptr_align = tcx.data_layout.pointer_align.abi;
let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap();
- let mut vtable = Allocation::uninit(vtable_size, ptr_align, /* panic_on_fail */ true).unwrap();
+ let mut vtable = Allocation::uninit(vtable_size, ptr_align);
// No need to do any alignment checks on the memory accesses below, because we know the
// allocation is correctly aligned as we created it above. Also we're only offsetting by
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index c62c33d4d..b0961d917 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -16,7 +16,7 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> {
fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
// SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
// FIXME: Represent the above fact in the trait system somehow.
- unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error_misc()) }
+ unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_misc_error(tcx)) }
}
}
@@ -34,7 +34,7 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self {
- let err = tcx.ty_error_misc();
+ let err = Ty::new_misc_error(tcx);
let arity = if let Some(frame) = stack.get(0)
&& frame.query.dep_kind == DepKind::fn_sig
@@ -96,19 +96,22 @@ impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability {
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<Ty<'_>> {
fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
- ty::EarlyBinder(Ty::from_cycle_error(tcx, cycle))
+ ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle))
}
}
impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
- ty::EarlyBinder(ty::Binder::from_cycle_error(tcx, cycle))
+ ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle))
}
}
-impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, ty::layout::LayoutError<'_>> {
+impl<'tcx, T> Value<TyCtxt<'tcx>, DepKind> for Result<T, &'_ ty::layout::LayoutError<'_>> {
fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo<DepKind>]) -> Self {
- Err(ty::layout::LayoutError::Cycle)
+ // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
+ // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
+ // tcx.arena.alloc is pretty much equal to leaking).
+ Err(Box::leak(Box::new(ty::layout::LayoutError::Cycle)))
}
}
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index cb265cf2c..98156840d 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -214,6 +214,9 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
mir_build_non_const_path = runtime values cannot be referenced in patterns
+mir_build_non_exhaustive_match_all_arms_guarded =
+ match arms with guards don't count towards exhaustivity
+
mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly
.help = ensure that all variants are matched explicitly by adding the suggested match arms
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs
index 32c618828..e5c2cc6c7 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/build/custom/mod.rs
@@ -118,7 +118,11 @@ fn parse_attribute(attr: &Attribute) -> MirPhase {
phase = Some(value);
}
other => {
- panic!("Unexpected key {}", other);
+ span_bug!(
+ nested.span(),
+ "Unexpected key while parsing custom_mir attribute: '{}'",
+ other
+ );
}
}
}
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index ebf830cb9..4cb9d7bab 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -128,7 +128,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
destination,
target: Some(target),
unwind: UnwindAction::Continue,
- from_hir_call: *from_hir_call,
+ call_source: if *from_hir_call { CallSource::Normal } else {
+ CallSource::OverloadedOperator
+ },
fn_span: *fn_span,
})
},
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 4d99ab4b0..3fe751ae0 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -52,7 +52,7 @@ pub fn as_constant_inner<'tcx>(
match lit_to_mir_constant(tcx, LitToConstInput { lit: &lit.node, ty, neg }) {
Ok(c) => c,
Err(LitToConstError::Reported(guar)) => {
- ConstantKind::Ty(tcx.const_error(ty, guar))
+ ConstantKind::Ty(ty::Const::new_error(tcx, guar, ty))
}
Err(LitToConstError::TypeError) => {
bug!("encountered type error in `lit_to_mir_constant`")
@@ -84,7 +84,7 @@ pub fn as_constant_inner<'tcx>(
Constant { user_ty, span, literal }
}
ExprKind::ConstParam { param, def_id: _ } => {
- let const_param = tcx.mk_const(ty::ConstKind::Param(param), expr.ty);
+ let const_param = ty::Const::new_param(tcx, param, expr.ty);
let literal = ConstantKind::Ty(const_param);
Constant { user_ty: None, span, literal }
@@ -106,7 +106,7 @@ pub fn as_constant_inner<'tcx>(
}
#[instrument(skip(tcx, lit_input))]
-pub(crate) fn lit_to_mir_constant<'tcx>(
+fn lit_to_mir_constant<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
) -> Result<ConstantKind<'tcx>, LitToConstError> {
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 7ec57add6..60acd279f 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -535,7 +535,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Cast { .. }
| ExprKind::Use { .. }
| ExprKind::NeverToAny { .. }
- | ExprKind::Pointer { .. }
+ | ExprKind::PointerCoercion { .. }
| ExprKind::Repeat { .. }
| ExprKind::Borrow { .. }
| ExprKind::AddressOf { .. }
@@ -549,6 +549,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
+ | ExprKind::Become { .. }
| ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
@@ -677,21 +678,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// check that we just did stays valid. Since we can't assign to
// unsized values, we only need to ensure that none of the
// pointers in the base place are modified.
- for (idx, elem) in base_place.projection.iter().enumerate().rev() {
+ for (base_place, elem) in base_place.iter_projections().rev() {
match elem {
ProjectionElem::Deref => {
- let fake_borrow_deref_ty = Place::ty_from(
- base_place.local,
- &base_place.projection[..idx],
- &self.local_decls,
- tcx,
- )
- .ty;
+ let fake_borrow_deref_ty = base_place.ty(&self.local_decls, tcx).ty;
let fake_borrow_ty =
- tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
let fake_borrow_temp =
self.local_decls.push(LocalDecl::new(fake_borrow_ty, expr_span));
- let projection = tcx.mk_place_elems(&base_place.projection[..idx]);
+ let projection = tcx.mk_place_elems(&base_place.projection);
self.cfg.push_assign(
block,
source_info,
@@ -705,12 +700,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temps.push(fake_borrow_temp);
}
ProjectionElem::Index(_) => {
- let index_ty = Place::ty_from(
- base_place.local,
- &base_place.projection[..idx],
- &self.local_decls,
- tcx,
- );
+ let index_ty = base_place.ty(&self.local_decls, tcx);
match index_ty.ty.kind() {
// The previous index expression has already
// done any index expressions needed here.
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 3742d640e..32ffb990b 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -162,7 +162,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
[],
expr_span,
);
- let storage = this.temp(tcx.mk_mut_ptr(tcx.types.u8), expr_span);
+ let storage = this.temp(Ty::new_mut_ptr(tcx, tcx.types.u8), expr_span);
let success = this.cfg.start_new_block();
this.cfg.terminate(
block,
@@ -173,7 +173,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination: storage,
target: Some(success),
unwind: UnwindAction::Continue,
- from_hir_call: false,
+ call_source: CallSource::Misc,
fn_span: expr_span,
},
);
@@ -300,7 +300,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let cast_kind = mir_cast_kind(ty, expr.ty);
block.and(Rvalue::Cast(cast_kind, source, expr.ty))
}
- ExprKind::Pointer { cast, source } => {
+ ExprKind::PointerCoercion { cast, source } => {
let source = unpack!(
block = this.as_operand(
block,
@@ -310,7 +310,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
NeedsTemporary::No
)
);
- block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
+ block.and(Rvalue::Cast(CastKind::PointerCoercion(cast), source, expr.ty))
}
ExprKind::Array { ref fields } => {
// (*) We would (maybe) be closer to codegen if we
@@ -442,7 +442,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match upvar.kind {
ExprKind::Borrow {
borrow_kind:
- BorrowKind::Mut { allow_two_phase_borrow: false },
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
arg,
} => unpack!(
block = this.limit_capture_mutability(
@@ -532,6 +532,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
+ | ExprKind::Become { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {
@@ -563,7 +564,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let bool_ty = self.tcx.types.bool;
let rvalue = match op {
BinOp::Add | BinOp::Sub | BinOp::Mul if self.check_overflow && ty.is_integral() => {
- let result_tup = self.tcx.mk_tup(&[ty, bool_ty]);
+ let result_tup = Ty::new_tup(self.tcx, &[ty, bool_ty]);
let result_value = self.temp(result_tup, span);
self.cfg.push_assign(
@@ -597,7 +598,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let (unsigned_rhs, unsigned_ty) = match rhs_ty.kind() {
ty::Uint(_) => (rhs.to_copy(), rhs_ty),
ty::Int(int_width) => {
- let uint_ty = self.tcx.mk_mach_uint(int_width.to_unsigned());
+ let uint_ty = Ty::new_uint(self.tcx, int_width.to_unsigned());
let rhs_temp = self.temp(uint_ty, span);
self.cfg.push_assign(
block,
@@ -795,8 +796,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
let borrow_kind = match mutability {
- Mutability::Not => BorrowKind::Unique,
- Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ Mutability::Not => BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },
+ Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
};
let arg_place = arg_place_builder.to_place(this);
diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs
index d9aa461c1..e07ba6b6e 100644
--- a/compiler/rustc_mir_build/src/build/expr/category.rs
+++ b/compiler/rustc_mir_build/src/build/expr/category.rs
@@ -63,7 +63,7 @@ impl Category {
| ExprKind::Binary { .. }
| ExprKind::Box { .. }
| ExprKind::Cast { .. }
- | ExprKind::Pointer { .. }
+ | ExprKind::PointerCoercion { .. }
| ExprKind::Repeat { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
@@ -82,7 +82,8 @@ impl Category {
| ExprKind::Block { .. }
| ExprKind::Break { .. }
| ExprKind::Continue { .. }
- | ExprKind::Return { .. } =>
+ | ExprKind::Return { .. }
+ | ExprKind::Become { .. } =>
// FIXME(#27840) these probably want their own
// category, like "nonterminating"
{
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 29ff916d2..e30fdcbbe 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -277,7 +277,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.ty
.is_inhabited_from(this.tcx, this.parent_module, this.param_env)
.then_some(success),
- from_hir_call,
+ call_source: if from_hir_call {
+ CallSource::Normal
+ } else {
+ CallSource::OverloadedOperator
+ },
fn_span,
},
);
@@ -489,7 +493,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.unit()
}
- ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
+ ExprKind::Continue { .. }
+ | ExprKind::Break { .. }
+ | ExprKind::Return { .. }
+ | ExprKind::Become { .. } => {
unpack!(block = this.stmt_expr(block, expr, None));
// No assign, as these have type `!`.
block.unit()
@@ -549,7 +556,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Binary { .. }
| ExprKind::Box { .. }
| ExprKind::Cast { .. }
- | ExprKind::Pointer { .. }
+ | ExprKind::PointerCoercion { .. }
| ExprKind::Repeat { .. }
| ExprKind::Array { .. }
| ExprKind::Tuple { .. }
diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs
index ea5aeb67d..396f82c27 100644
--- a/compiler/rustc_mir_build/src/build/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs
@@ -99,6 +99,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
BreakableTarget::Return,
source_info,
),
+ // FIXME(explicit_tail_calls): properly lower tail calls here
+ ExprKind::Become { value } => this.break_scope(
+ block,
+ Some(&this.thir[value]),
+ BreakableTarget::Return,
+ source_info,
+ ),
_ => {
assert!(
statement_scope.is_some(),
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 6df06df5c..10770213c 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1751,7 +1751,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
projection: tcx.mk_place_elems(matched_place_ref.projection),
};
let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
- let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
+ let fake_borrow_ty =
+ Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fake_borrow_deref_ty);
let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
fake_borrow_temp.internal = self.local_decls[matched_place.local].internal;
fake_borrow_temp.local_info = ClearCrossCrate::Set(Box::new(LocalInfo::FakeBorrow));
@@ -2250,7 +2251,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// This variable isn't mutated but has a name, so has to be
// immutable to avoid the unused mut lint.
mutability: Mutability::Not,
- ty: tcx.mk_imm_ref(tcx.lifetimes.re_erased, var_ty),
+ ty: Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, var_ty),
user_ty: None,
source_info,
internal: false,
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index dbdb5b4a9..e6806177d 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -16,7 +16,7 @@ use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::GenericArg;
-use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
@@ -244,8 +244,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
bug!("matching on `String` went through without enabling string_deref_patterns");
}
let re_erased = tcx.lifetimes.re_erased;
- let ref_string = self.temp(tcx.mk_imm_ref(re_erased, ty), test.span);
- let ref_str_ty = tcx.mk_imm_ref(re_erased, tcx.types.str_);
+ let ref_string = self.temp(Ty::new_imm_ref(tcx,re_erased, ty), test.span);
+ let ref_str_ty = Ty::new_imm_ref(tcx,re_erased, tcx.types.str_);
let ref_str = self.temp(ref_str_ty, test.span);
let deref = tcx.require_lang_item(LangItem::Deref, None);
let method = trait_method(tcx, deref, sym::deref, [ty]);
@@ -264,7 +264,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination: ref_str,
target: Some(eq_block),
unwind: UnwindAction::Continue,
- from_hir_call: false,
+ call_source: CallSource::Misc,
fn_span: source_info.span
}
);
@@ -415,7 +415,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
let tcx = self.tcx;
// make both a slice
- ty = tcx.mk_imm_ref(*region, tcx.mk_slice(*elem_ty));
+ ty = Ty::new_imm_ref(tcx, *region, Ty::new_slice(tcx, *elem_ty));
if opt_ref_ty.is_some() {
let temp = self.temp(ty, source_info.span);
self.cfg.push_assign(
@@ -423,7 +423,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info,
temp,
Rvalue::Cast(
- CastKind::Pointer(PointerCast::Unsize),
+ CastKind::PointerCoercion(PointerCoercion::Unsize),
Operand::Copy(val),
ty,
),
@@ -436,7 +436,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
source_info,
slice,
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), expect, ty),
+ Rvalue::Cast(
+ CastKind::PointerCoercion(PointerCoercion::Unsize),
+ expect,
+ ty,
+ ),
);
expect = Operand::Move(slice);
}
@@ -449,7 +453,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// non_scalar_compare called on non-reference type
let temp = self.temp(ty, source_info.span);
self.cfg.push_assign(block, source_info, temp, Rvalue::Use(expect));
- let ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, ty);
+ let ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, ty);
let ref_temp = self.temp(ref_ty, source_info.span);
self.cfg.push_assign(
@@ -496,7 +500,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination: eq_result,
target: Some(eq_block),
unwind: UnwindAction::Continue,
- from_hir_call: false,
+ call_source: CallSource::MatchCmp,
fn_span: source_info.span,
},
);
@@ -871,7 +875,7 @@ fn trait_method<'tcx>(
.find(|item| item.kind == ty::AssocKind::Fn)
.expect("trait method not found");
- let method_ty = tcx.mk_fn_def(item.def_id, substs);
+ let method_ty = Ty::new_fn_def(tcx, item.def_id, substs);
ConstantKind::zero_sized(method_ty)
}
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 4e3e98b56..d828e71c7 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -1,4 +1,3 @@
-pub(crate) use crate::build::expr::as_constant::lit_to_mir_constant;
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
use rustc_apfloat::ieee::{Double, Single};
@@ -37,6 +36,22 @@ pub(crate) fn mir_built(
tcx.alloc_steal_mir(mir_build(tcx, def))
}
+pub(crate) fn closure_saved_names_of_captured_variables<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+) -> IndexVec<FieldIdx, Symbol> {
+ tcx.closure_captures(def_id)
+ .iter()
+ .map(|captured_place| {
+ let name = captured_place.to_symbol();
+ match captured_place.info.capture_kind {
+ ty::UpvarCapture::ByValue => name,
+ ty::UpvarCapture::ByRef(..) => Symbol::intern(&format!("_ref__{name}")),
+ }
+ })
+ .collect()
+}
+
/// Construct the MIR for a given `DefId`.
fn mir_build(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> {
// Ensure unsafeck and abstract const building is ran before we steal the THIR.
@@ -557,7 +572,7 @@ fn construct_const<'a, 'tcx>(
span,
..
}) => (*span, ty.span),
- Node::AnonConst(_) => {
+ Node::AnonConst(_) | Node::ConstBlock(_) => {
let span = tcx.def_span(def);
(span, span)
}
@@ -599,7 +614,7 @@ fn construct_error(tcx: TyCtxt<'_>, def: LocalDefId, err: ErrorGuaranteed) -> Bo
let generator_kind = tcx.generator_kind(def);
let body_owner_kind = tcx.hir().body_owner_kind(def);
- let ty = tcx.ty_error(err);
+ let ty = Ty::new_error(tcx, err);
let num_params = match body_owner_kind {
hir::BodyOwnerKind::Fn => tcx.fn_sig(def).skip_binder().inputs().skip_binder().len(),
hir::BodyOwnerKind::Closure => {
@@ -927,7 +942,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match self.unit_temp {
Some(tmp) => tmp,
None => {
- let ty = self.tcx.mk_unit();
+ let ty = Ty::new_unit(self.tcx);
let fn_span = self.fn_span;
let tmp = self.temp(ty, fn_span);
self.unit_temp = Some(tmp);
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 7c0fbc6f8..72374102c 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -91,6 +91,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::{Expr, LintLevel};
+use rustc_middle::ty::Ty;
use rustc_span::{Span, DUMMY_SP};
#[derive(Debug)]
@@ -724,7 +725,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Add a dummy `Assign` statement to the CFG, with the span for the source code's `continue`
// statement.
fn add_dummy_assignment(&mut self, span: Span, block: BasicBlock, source_info: SourceInfo) {
- let local_decl = LocalDecl::new(self.tcx.mk_unit(), span).internal();
+ let local_decl = LocalDecl::new(Ty::new_unit(self.tcx), span).internal();
let temp_place = Place::from(self.local_decls.push(local_decl));
self.cfg.push_assign_unit(block, source_info, temp_place, self.tcx);
}
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 0506f2bf2..6b2b140fa 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -254,7 +254,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
);
};
match borrow_kind {
- BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {
+ BorrowKind::Shallow | BorrowKind::Shared => {
if !ty.is_freeze(self.tcx, self.param_env) {
self.requires_unsafe(pat.span, BorrowOfLayoutConstrainedField);
}
@@ -303,7 +303,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::NeverToAny { .. }
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. }
- | ExprKind::Pointer { .. }
+ | ExprKind::PointerCoercion { .. }
| ExprKind::Repeat { .. }
| ExprKind::StaticRef { .. }
| ExprKind::ThreadLocalRef { .. }
@@ -316,6 +316,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
| ExprKind::Closure { .. }
| ExprKind::Continue { .. }
| ExprKind::Return { .. }
+ | ExprKind::Become { .. }
| ExprKind::Yield { .. }
| ExprKind::Loop { .. }
| ExprKind::Let { .. }
@@ -440,7 +441,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
visit::walk_expr(&mut visitor, expr);
if visitor.found {
match borrow_kind {
- BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique
+ BorrowKind::Shallow | BorrowKind::Shared
if !self.thir[arg].ty.is_freeze(self.tcx, self.param_env) =>
{
self.requires_unsafe(expr.span, BorrowOfLayoutConstrainedField)
@@ -448,7 +449,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
BorrowKind::Mut { .. } => {
self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField)
}
- BorrowKind::Shallow | BorrowKind::Shared | BorrowKind::Unique => {}
+ BorrowKind::Shallow | BorrowKind::Shared => {}
}
}
}
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 7c0df201b..df00cc75c 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -432,6 +432,10 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
}
}
+#[derive(Subdiagnostic)]
+#[note(mir_build_non_exhaustive_match_all_arms_guarded)]
+pub struct NonExhaustiveMatchAllArmsGuarded;
+
#[derive(Diagnostic)]
#[diag(mir_build_static_in_pattern, code = "E0158")]
pub struct StaticInPattern {
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index c964e62c9..4fdc3178c 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -32,8 +32,9 @@ fluent_messages! { "../messages.ftl" }
pub fn provide(providers: &mut Providers) {
providers.check_match = thir::pattern::check_match;
providers.lit_to_const = thir::constant::lit_to_const;
- providers.lit_to_mir_constant = build::lit_to_mir_constant;
providers.mir_built = build::mir_built;
+ providers.closure_saved_names_of_captured_variables =
+ build::closure_saved_names_of_captured_variables;
providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
providers.thir_body = thir::cx::thir_body;
providers.thir_tree = thir::print::thir_tree;
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index 57ae6a365..fbb74650f 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -3,6 +3,8 @@ use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
use rustc_span::DUMMY_SP;
+use crate::build::parse_float_into_scalar;
+
pub(crate) fn lit_to_const<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
@@ -46,12 +48,28 @@ pub(crate) fn lit_to_const<'tcx>(
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
ty::ValTree::from_scalar_int((*n).into())
}
+ (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
+ {
+ let bytes = data as &[u8];
+ ty::ValTree::from_raw_bytes(tcx, bytes)
+ }
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
let scalar_int =
trunc(if neg { (*n as i128).overflowing_neg().0 as u128 } else { *n })?;
ty::ValTree::from_scalar_int(scalar_int)
}
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
+ (ast::LitKind::Float(n, _), ty::Float(fty)) => {
+ let bits = parse_float_into_scalar(*n, *fty, neg)
+ .ok_or_else(|| {
+ LitToConstError::Reported(tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!("couldn't parse float literal: {:?}", lit_input.lit),
+ ))
+ })?
+ .assert_int();
+ ty::ValTree::from_scalar_int(bits)
+ }
(ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
(ast::LitKind::Err, _) => {
return Err(LitToConstError::Reported(
@@ -61,5 +79,5 @@ pub(crate) fn lit_to_const<'tcx>(
_ => return Err(LitToConstError::TypeError),
};
- Ok(tcx.mk_const(valtree, ty))
+ Ok(ty::Const::new_value(tcx, valtree, ty))
}
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index b20495d60..37537683f 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -13,7 +13,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp};
use rustc_middle::thir::*;
use rustc_middle::ty::adjustment::{
- Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCast,
+ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{
@@ -125,11 +125,16 @@ impl<'tcx> Cx<'tcx> {
};
let kind = match adjustment.kind {
- Adjust::Pointer(PointerCast::Unsize) => {
+ Adjust::Pointer(PointerCoercion::Unsize) => {
adjust_span(&mut expr);
- ExprKind::Pointer { cast: PointerCast::Unsize, source: self.thir.exprs.push(expr) }
+ ExprKind::PointerCoercion {
+ cast: PointerCoercion::Unsize,
+ source: self.thir.exprs.push(expr),
+ }
+ }
+ Adjust::Pointer(cast) => {
+ ExprKind::PointerCoercion { cast, source: self.thir.exprs.push(expr) }
}
- Adjust::Pointer(cast) => ExprKind::Pointer { cast, source: self.thir.exprs.push(expr) },
Adjust::NeverToAny if adjustment.target.is_never() => return expr,
Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) },
Adjust::Deref(None) => {
@@ -143,9 +148,11 @@ impl<'tcx> Cx<'tcx> {
expr = Expr {
temp_lifetime,
- ty: self
- .tcx
- .mk_ref(deref.region, ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl }),
+ ty: Ty::new_ref(
+ self.tcx,
+ deref.region,
+ ty::TypeAndMut { ty: expr.ty, mutbl: deref.mutbl },
+ ),
span,
kind: ExprKind::Borrow {
borrow_kind: deref.mutbl.to_borrow_kind(),
@@ -190,9 +197,9 @@ impl<'tcx> Cx<'tcx> {
// Special cased so that we can type check that the element
// type of the source matches the pointed to type of the
// destination.
- ExprKind::Pointer {
+ ExprKind::PointerCoercion {
source: self.mirror_expr(source),
- cast: PointerCast::ArrayToPointer,
+ cast: PointerCoercion::ArrayToPointer,
}
} else {
// check whether this is casting an enum variant discriminant
@@ -208,17 +215,18 @@ impl<'tcx> Cx<'tcx> {
// so we wouldn't have to compute and store the actual value
let hir::ExprKind::Path(ref qpath) = source.kind else {
- return ExprKind::Cast { source: self.mirror_expr(source)};
+ return ExprKind::Cast { source: self.mirror_expr(source) };
};
let res = self.typeck_results().qpath_res(qpath, source.hir_id);
let ty = self.typeck_results().node_type(source.hir_id);
let ty::Adt(adt_def, substs) = ty.kind() else {
- return ExprKind::Cast { source: self.mirror_expr(source)};
+ return ExprKind::Cast { source: self.mirror_expr(source) };
};
- let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res else {
- return ExprKind::Cast { source: self.mirror_expr(source)};
+ let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), variant_ctor_id) = res
+ else {
+ return ExprKind::Cast { source: self.mirror_expr(source) };
};
let idx = adt_def.variant_index_with_ctor_id(variant_ctor_id);
@@ -308,7 +316,7 @@ impl<'tcx> Cx<'tcx> {
let arg_tys = args.iter().map(|e| self.typeck_results().expr_ty_adjusted(e));
let tupled_args = Expr {
- ty: tcx.mk_tup_from_iter(arg_tys),
+ ty: Ty::new_tup_from_iter(tcx, arg_tys),
temp_lifetime,
span: expr.span,
kind: ExprKind::Tuple { fields: self.mirror_exprs(args) },
@@ -351,19 +359,35 @@ impl<'tcx> Cx<'tcx> {
});
}
}
- let adt_data =
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = fun.kind {
- // Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
- expr_ty.ty_adt_def().and_then(|adt_def| match path.res {
- Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
+
+ // Tuple-like ADTs are represented as ExprKind::Call. We convert them here.
+ let adt_data = if let hir::ExprKind::Path(ref qpath) = fun.kind
+ && let Some(adt_def) = expr_ty.ty_adt_def() {
+ match qpath {
+ hir::QPath::Resolved(_, ref path) => {
+ match path.res {
+ Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_id) => {
+ Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
+ }
+ Res::SelfCtor(..) => Some((adt_def, FIRST_VARIANT)),
+ _ => None,
+ }
+ }
+ hir::QPath::TypeRelative(_ty, _) => {
+ if let Some((DefKind::Ctor(_, CtorKind::Fn), ctor_id)) =
+ self.typeck_results().type_dependent_def(fun.hir_id)
+ {
Some((adt_def, adt_def.variant_index_with_ctor_id(ctor_id)))
+ } else {
+ None
}
- Res::SelfCtor(..) => Some((adt_def, FIRST_VARIANT)),
- _ => None,
- })
- } else {
- None
- };
+
+ }
+ _ => None,
+ }
+ } else {
+ None
+ };
if let Some((adt_def, index)) = adt_data {
let substs = self.typeck_results().node_substs(fun.hir_id);
let user_provided_types = self.typeck_results().user_provided_types();
@@ -694,7 +718,8 @@ impl<'tcx> Cx<'tcx> {
ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
}
- hir::ExprKind::Ret(ref v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
+ hir::ExprKind::Ret(v) => ExprKind::Return { value: v.map(|v| self.mirror_expr(v)) },
+ hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
Ok(target_id) => ExprKind::Break {
label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
@@ -854,7 +879,11 @@ impl<'tcx> Cx<'tcx> {
let user_ty = self.user_substs_applied_to_res(expr.hir_id, Res::Def(kind, def_id));
debug!("method_callee: user_ty={:?}", user_ty);
(
- self.tcx().mk_fn_def(def_id, self.typeck_results().node_substs(expr.hir_id)),
+ Ty::new_fn_def(
+ self.tcx(),
+ def_id,
+ self.typeck_results().node_substs(expr.hir_id),
+ ),
user_ty,
)
}
@@ -1007,7 +1036,7 @@ impl<'tcx> Cx<'tcx> {
let ty::Ref(region, _, mutbl) = *self.thir[args[0]].ty.kind() else {
span_bug!(span, "overloaded_place: receiver is not a reference");
};
- let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl });
+ let ref_ty = Ty::new_ref(self.tcx, region, ty::TypeAndMut { ty: place_ty, mutbl });
// construct the complete expression `foo()` for the overloaded call,
// which will yield the &T type
@@ -1095,8 +1124,12 @@ impl<'tcx> Cx<'tcx> {
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow {
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
- ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
- ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false },
+ ty::BorrowKind::UniqueImmBorrow => {
+ BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture }
+ }
+ ty::BorrowKind::MutBorrow => {
+ BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
+ }
};
Expr {
temp_lifetime,
@@ -1132,9 +1165,9 @@ impl ToBorrowKind for AutoBorrowMutability {
use rustc_middle::ty::adjustment::AllowTwoPhase;
match *self {
AutoBorrowMutability::Mut { allow_two_phase_borrow } => BorrowKind::Mut {
- allow_two_phase_borrow: match allow_two_phase_borrow {
- AllowTwoPhase::Yes => true,
- AllowTwoPhase::No => false,
+ kind: match allow_two_phase_borrow {
+ AllowTwoPhase::Yes => mir::MutBorrowKind::TwoPhaseBorrow,
+ AllowTwoPhase::No => mir::MutBorrowKind::Default,
},
},
AutoBorrowMutability::Not => BorrowKind::Shared,
@@ -1145,7 +1178,7 @@ impl ToBorrowKind for AutoBorrowMutability {
impl ToBorrowKind for hir::Mutability {
fn to_borrow_kind(&self) -> BorrowKind {
match *self {
- hir::Mutability::Mut => BorrowKind::Mut { allow_two_phase_borrow: false },
+ hir::Mutability::Mut => BorrowKind::Mut { kind: mir::MutBorrowKind::Default },
hir::Mutability::Not => BorrowKind::Shared,
}
}
diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs
index 463f639de..e6a98d1aa 100644
--- a/compiler/rustc_mir_build/src/thir/cx/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs
@@ -15,7 +15,7 @@ use rustc_hir::HirId;
use rustc_hir::Node;
use rustc_middle::middle::region;
use rustc_middle::thir::*;
-use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
+use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt};
use rustc_span::Span;
pub(crate) fn thir_body(
@@ -40,7 +40,7 @@ pub(crate) fn thir_body(
// It will always be `()` in this case.
if tcx.def_kind(owner_def) == DefKind::Generator && body.params.is_empty() {
cx.thir.params.push(Param {
- ty: tcx.mk_unit(),
+ ty: Ty::new_unit(tcx),
pat: None,
ty_span: None,
self_kind: None,
@@ -142,7 +142,7 @@ impl<'tcx> Cx<'tcx> {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BrEnv,
};
- let env_region = self.tcx.mk_re_late_bound(ty::INNERMOST, br);
+ let env_region = ty::Region::new_late_bound(self.tcx, ty::INNERMOST, br);
let closure_env_ty =
self.tcx.closure_env_ty(closure_def_id, closure_substs, env_region).unwrap();
let liberated_closure_env_ty = self.tcx.erase_late_bound_regions(
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 1e51cb9aa..ef60f08bf 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -443,7 +443,12 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let mut let_suggestion = None;
let mut misc_suggestion = None;
let mut interpreted_as_const = None;
- if let PatKind::Constant { .. } = pat.kind
+
+ if let PatKind::Constant { .. }
+ | PatKind::AscribeUserType {
+ subpattern: box Pat { kind: PatKind::Constant { .. }, .. },
+ ..
+ } = pat.kind
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
{
// If the pattern to match is an integer literal:
@@ -825,6 +830,11 @@ fn non_exhaustive_match<'p, 'tcx>(
_ => " or multiple match arms",
},
);
+
+ let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
+ if !is_empty_match && all_arms_have_guards {
+ err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
+ }
if let Some((span, sugg)) = suggestion {
err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
} else {
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 b243f1dc8..050b01294 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
@@ -1,13 +1,14 @@
use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
use rustc_index::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
use rustc_middle::mir;
use rustc_middle::thir::{FieldPat, Pat, PatKind};
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt, ValTree};
use rustc_session::lint;
use rustc_span::Span;
-use rustc_target::abi::FieldIdx;
+use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, ObligationCause};
@@ -29,11 +30,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
cv: mir::ConstantKind<'tcx>,
id: hir::HirId,
span: Span,
- mir_structural_match_violation: bool,
+ check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> {
let infcx = self.tcx.infer_ctxt().build();
let mut convert = ConstToPat::new(self, id, span, infcx);
- convert.to_pat(cv, mir_structural_match_violation)
+ convert.to_pat(cv, check_body_for_struct_match_violation)
}
}
@@ -104,7 +105,7 @@ impl<'tcx> ConstToPat<'tcx> {
fn to_pat(
&mut self,
cv: mir::ConstantKind<'tcx>,
- mir_structural_match_violation: bool,
+ check_body_for_struct_match_violation: Option<DefId>,
) -> Box<Pat<'tcx>> {
trace!(self.treat_byte_string_as_slice);
// This method is just a wrapper handling a validity check; the heavy lifting is
@@ -114,14 +115,44 @@ impl<'tcx> ConstToPat<'tcx> {
// once indirect_structural_match is a full fledged error, this
// level of indirection can be eliminated
- let inlined_const_as_pat =
- self.recur(cv, mir_structural_match_violation).unwrap_or_else(|_| {
- Box::new(Pat {
- span: self.span,
- ty: cv.ty(),
- kind: PatKind::Constant { value: cv },
- })
- });
+ let mir_structural_match_violation = check_body_for_struct_match_violation.map(|def_id| {
+ // `mir_const_qualif` must be called with the `DefId` of the item where the const is
+ // defined, not where it is declared. The difference is significant for associated
+ // constants.
+ self.tcx().mir_const_qualif(def_id).custom_eq
+ });
+ debug!(?check_body_for_struct_match_violation, ?mir_structural_match_violation);
+
+ let inlined_const_as_pat = match cv {
+ mir::ConstantKind::Ty(c) => match c.kind() {
+ ty::ConstKind::Param(_)
+ | ty::ConstKind::Infer(_)
+ | ty::ConstKind::Bound(_, _)
+ | ty::ConstKind::Placeholder(_)
+ | ty::ConstKind::Unevaluated(_)
+ | ty::ConstKind::Error(_)
+ | ty::ConstKind::Expr(_) => {
+ span_bug!(self.span, "unexpected const in `to_pat`: {:?}", c.kind())
+ }
+ ty::ConstKind::Value(valtree) => self
+ .recur(valtree, cv.ty(), mir_structural_match_violation.unwrap_or(false))
+ .unwrap_or_else(|_| {
+ Box::new(Pat {
+ span: self.span,
+ ty: cv.ty(),
+ kind: PatKind::Constant { value: cv },
+ })
+ }),
+ },
+ mir::ConstantKind::Unevaluated(_, _) => {
+ span_bug!(self.span, "unevaluated const in `to_pat`: {cv:?}")
+ }
+ mir::ConstantKind::Val(_, _) => Box::new(Pat {
+ span: self.span,
+ ty: cv.ty(),
+ kind: PatKind::Constant { value: cv },
+ }),
+ };
if !self.saw_const_match_error.get() {
// If we were able to successfully convert the const to some pat,
@@ -141,29 +172,70 @@ impl<'tcx> ConstToPat<'tcx> {
//
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation`.
- if structural.is_none() && mir_structural_match_violation {
+ if structural.is_none() && mir_structural_match_violation.unwrap_or(false) {
warn!("MIR const-checker found novel structural match violation. See #73448.");
return inlined_const_as_pat;
}
if let Some(non_sm_ty) = structural {
if !self.type_may_have_partial_eq_impl(cv.ty()) {
- // fatal avoids ICE from resolution of nonexistent method (rare case).
- self.tcx()
- .sess
- .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
- } else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
- self.tcx().emit_spanned_lint(
- lint::builtin::INDIRECT_STRUCTURAL_MATCH,
- self.id,
- self.span,
- IndirectStructuralMatch { non_sm_ty },
- );
- } else {
- debug!(
- "`search_for_structural_match_violation` found one, but `CustomEq` was \
- not in the qualifs for that `const`"
- );
+ if let ty::Adt(def, ..) = non_sm_ty.kind() {
+ if def.is_union() {
+ let err = UnionPattern { span: self.span };
+ self.tcx().sess.emit_err(err);
+ } else {
+ // fatal avoids ICE from resolution of nonexistent method (rare case).
+ self.tcx()
+ .sess
+ .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty });
+ }
+ } else {
+ let err = InvalidPattern { span: self.span, non_sm_ty };
+ self.tcx().sess.emit_err(err);
+ return Box::new(Pat { span: self.span, ty: cv.ty(), kind: PatKind::Wild });
+ }
+ } else if !self.saw_const_match_lint.get() {
+ if let Some(mir_structural_match_violation) = mir_structural_match_violation {
+ match non_sm_ty.kind() {
+ ty::RawPtr(pointee)
+ if pointee.ty.is_sized(self.tcx(), self.param_env) => {}
+ ty::FnPtr(..) | ty::RawPtr(..) => {
+ self.tcx().emit_spanned_lint(
+ lint::builtin::POINTER_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ PointerPattern,
+ );
+ }
+ ty::Adt(..) if mir_structural_match_violation => {
+ self.tcx().emit_spanned_lint(
+ lint::builtin::INDIRECT_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ IndirectStructuralMatch { non_sm_ty },
+ );
+ }
+ _ => {
+ debug!(
+ "`search_for_structural_match_violation` found one, but `CustomEq` was \
+ not in the qualifs for that `const`"
+ );
+ }
+ }
+ }
+ }
+ } else if !self.saw_const_match_lint.get() {
+ match cv.ty().kind() {
+ ty::RawPtr(pointee) if pointee.ty.is_sized(self.tcx(), self.param_env) => {}
+ ty::FnPtr(..) | ty::RawPtr(..) => {
+ self.tcx().emit_spanned_lint(
+ lint::builtin::POINTER_STRUCTURAL_MATCH,
+ self.id,
+ self.span,
+ PointerPattern,
+ );
+ }
+ _ => {}
}
}
}
@@ -171,6 +243,7 @@ impl<'tcx> ConstToPat<'tcx> {
inlined_const_as_pat
}
+ #[instrument(level = "trace", skip(self), ret)]
fn type_may_have_partial_eq_impl(&self, ty: Ty<'tcx>) -> bool {
// double-check there even *is* a semantic `PartialEq` to dispatch to.
//
@@ -187,29 +260,19 @@ impl<'tcx> ConstToPat<'tcx> {
);
// FIXME: should this call a `predicate_must_hold` variant instead?
- let has_impl = self.infcx.predicate_may_hold(&partial_eq_obligation);
-
- // Note: To fix rust-lang/rust#65466, we could just remove this type
- // walk hack for function pointers, and unconditionally error
- // if `PartialEq` is not implemented. However, that breaks stable
- // code at the moment, because types like `for <'a> fn(&'a ())` do
- // not *yet* implement `PartialEq`. So for now we leave this here.
- has_impl
- || ty.walk().any(|t| match t.unpack() {
- ty::subst::GenericArgKind::Lifetime(_) => false,
- ty::subst::GenericArgKind::Type(t) => t.is_fn_ptr(),
- ty::subst::GenericArgKind::Const(_) => false,
- })
+ self.infcx.predicate_may_hold(&partial_eq_obligation)
}
fn field_pats(
&self,
- vals: impl Iterator<Item = mir::ConstantKind<'tcx>>,
+ vals: impl Iterator<Item = (ValTree<'tcx>, Ty<'tcx>)>,
) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
vals.enumerate()
- .map(|(idx, val)| {
+ .map(|(idx, (val, ty))| {
let field = FieldIdx::new(idx);
- Ok(FieldPat { field, pattern: self.recur(val, false)? })
+ // Patterns can only use monomorphic types.
+ let ty = self.tcx().normalize_erasing_regions(self.param_env, ty);
+ Ok(FieldPat { field, pattern: self.recur(val, ty, false)? })
})
.collect()
}
@@ -218,7 +281,8 @@ impl<'tcx> ConstToPat<'tcx> {
#[instrument(skip(self), level = "debug")]
fn recur(
&self,
- cv: mir::ConstantKind<'tcx>,
+ cv: ValTree<'tcx>,
+ ty: Ty<'tcx>,
mir_structural_match_violation: bool,
) -> Result<Box<Pat<'tcx>>, FallbackToConstRef> {
let id = self.id;
@@ -226,8 +290,9 @@ impl<'tcx> ConstToPat<'tcx> {
let tcx = self.tcx();
let param_env = self.param_env;
- let kind = match cv.ty().kind() {
+ let kind = match ty.kind() {
ty::Float(_) => {
+ self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
@@ -236,27 +301,6 @@ impl<'tcx> ConstToPat<'tcx> {
);
return Err(FallbackToConstRef);
}
- ty::Adt(adt_def, _) if adt_def.is_union() => {
- // Matching on union fields is unsafe, we can't hide it in constants
- self.saw_const_match_error.set(true);
- let err = UnionPattern { span };
- tcx.sess.emit_err(err);
- PatKind::Wild
- }
- ty::Adt(..)
- if !self.type_may_have_partial_eq_impl(cv.ty())
- // FIXME(#73448): Find a way to bring const qualification into parity with
- // `search_for_structural_match_violation` and then remove this condition.
-
- // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
- // could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
- && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
- {
- self.saw_const_match_error.set(true);
- let err = TypeNotStructural { span, non_sm_ty };
- tcx.sess.emit_err(err);
- PatKind::Wild
- }
// If the type is not structurally comparable, just emit the constant directly,
// causing the pattern match code to treat it opaquely.
// FIXME: This code doesn't emit errors itself, the caller emits the errors.
@@ -266,16 +310,14 @@ impl<'tcx> ConstToPat<'tcx> {
// details.
// Backwards compatibility hack because we can't cause hard errors on these
// types, so we compare them via `PartialEq::eq` at runtime.
- ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
- if !self.saw_const_match_error.get()
- && !self.saw_const_match_lint.get()
- {
+ ty::Adt(..) if !self.type_marked_structural(ty) && self.behind_reference.get() => {
+ if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
- IndirectStructuralMatch { non_sm_ty: cv.ty() },
+ IndirectStructuralMatch { non_sm_ty: ty },
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
@@ -283,108 +325,64 @@ impl<'tcx> ConstToPat<'tcx> {
// `PartialEq::eq` on it.
return Err(FallbackToConstRef);
}
- ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
- debug!(
- "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
- adt_def,
- cv.ty()
- );
+ ty::Adt(adt_def, _) if !self.type_marked_structural(ty) => {
+ debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, ty,);
self.saw_const_match_error.set(true);
- let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
+ let err = TypeNotStructural { span, non_sm_ty: ty };
tcx.sess.emit_err(err);
PatKind::Wild
}
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
- let destructured = tcx.destructure_mir_constant(param_env, cv);
-
+ let (&variant_index, fields) = cv.unwrap_branch().split_first().unwrap();
+ let variant_index =
+ VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
PatKind::Variant {
adt_def: *adt_def,
substs,
- variant_index: destructured
- .variant
- .expect("destructed const of adt without variant id"),
- subpatterns: self.field_pats(destructured.fields.iter().copied())?,
+ variant_index,
+ subpatterns: self.field_pats(
+ fields.iter().copied().zip(
+ adt_def.variants()[variant_index]
+ .fields
+ .iter()
+ .map(|field| field.ty(self.tcx(), substs)),
+ ),
+ )?,
}
}
- ty::Tuple(_) | ty::Adt(_, _) => {
- let destructured = tcx.destructure_mir_constant(param_env, cv);
- PatKind::Leaf { subpatterns: self.field_pats(destructured.fields.iter().copied())? }
- }
- ty::Array(..) => PatKind::Array {
- prefix: tcx
- .destructure_mir_constant(param_env, cv)
- .fields
+ ty::Tuple(fields) => PatKind::Leaf {
+ subpatterns: self
+ .field_pats(cv.unwrap_branch().iter().copied().zip(fields.iter()))?,
+ },
+ ty::Adt(def, substs) => PatKind::Leaf {
+ subpatterns: self.field_pats(cv.unwrap_branch().iter().copied().zip(
+ def.non_enum_variant().fields.iter().map(|field| field.ty(self.tcx(), substs)),
+ ))?,
+ },
+ ty::Slice(elem_ty) => PatKind::Slice {
+ prefix: cv
+ .unwrap_branch()
+ .iter()
+ .map(|val| self.recur(*val, *elem_ty, false))
+ .collect::<Result<_, _>>()?,
+ slice: None,
+ suffix: Box::new([]),
+ },
+ ty::Array(elem_ty, _) => PatKind::Array {
+ prefix: cv
+ .unwrap_branch()
.iter()
- .map(|val| self.recur(*val, false))
+ .map(|val| self.recur(*val, *elem_ty, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Box::new([]),
},
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
- // These are not allowed and will error elsewhere anyway.
- ty::Dynamic(..) => {
- self.saw_const_match_error.set(true);
- let err = InvalidPattern { span, non_sm_ty: cv.ty() };
- tcx.sess.emit_err(err);
- PatKind::Wild
- }
- // `&str` is represented as `ConstValue::Slice`, let's keep using this
+ // `&str` is represented as a valtree, let's keep using this
// optimization for now.
- ty::Str => 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.
- // 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_mir_constant(self.param_env.and(cv));
- let val = PatKind::Deref {
- subpattern: Box::new(Pat {
- kind: PatKind::Array {
- prefix: tcx
- .destructure_mir_constant(param_env, array)
- .fields
- .iter()
- .map(|val| self.recur(*val, false))
- .collect::<Result<_, _>>()?,
- slice: None,
- suffix: Box::new([]),
- },
- 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, 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_mir_constant(self.param_env.and(cv));
- let val = PatKind::Deref {
- subpattern: Box::new(Pat {
- kind: PatKind::Slice {
- prefix: tcx
- .destructure_mir_constant(param_env, array)
- .fields
- .iter()
- .map(|val| self.recur(*val, false))
- .collect::<Result<_, _>>()?,
- slice: None,
- suffix: Box::new([]),
- },
- span,
- ty: tcx.mk_slice(elem_ty),
- }),
- };
- self.behind_reference.set(old);
- val
- }
+ ty::Str => PatKind::Constant {
+ value: mir::ConstantKind::Ty(ty::Const::new_value(tcx, cv, ty)),
+ },
// Backwards compatibility hack: support references to non-structural types,
// but hard error if we aren't behind a double reference. We could just use
// the fallback code path below, but that would allow *more* of this fishy
@@ -392,11 +390,9 @@ impl<'tcx> ConstToPat<'tcx> {
// instead of a hard error.
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() {
- if !self.saw_const_match_error.get()
- && !self.saw_const_match_lint.get()
- {
- self.saw_const_match_lint.set(true);
- tcx.emit_spanned_lint(
+ if !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() {
+ self.saw_const_match_lint.set(true);
+ tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
span,
@@ -417,49 +413,41 @@ impl<'tcx> ConstToPat<'tcx> {
// convert the dereferenced constant to a pattern that is the sub-pattern of the
// deref pattern.
_ => {
- if !pointee_ty.is_sized(tcx, param_env) {
- // `tcx.deref_mir_constant()` below will ICE with an unsized type
- // (except slices, which are handled in a separate arm above).
-
+ if !pointee_ty.is_sized(tcx, param_env) && !pointee_ty.is_slice() {
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
tcx.sess.emit_err(err);
+ // FIXME: introduce PatKind::Error to silence follow up diagnostics due to unreachable patterns.
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
- let subpattern = self.recur(tcx.deref_mir_constant(self.param_env.and(cv)), false)?;
+ // `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.
+ // 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.
+ let pointee_ty = match *pointee_ty.kind() {
+ ty::Array(elem_ty, _) if self.treat_byte_string_as_slice => {
+ Ty::new_slice(tcx, elem_ty)
+ }
+ _ => *pointee_ty,
+ };
+ // References have the same valtree representation as their pointee.
+ let subpattern = self.recur(cv, pointee_ty, false)?;
self.behind_reference.set(old);
PatKind::Deref { subpattern }
}
}
},
- ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => {
- PatKind::Constant { value: cv }
- }
- ty::RawPtr(pointee) if pointee.ty.is_sized(tcx, param_env) => {
- return Err(FallbackToConstRef);
- }
- // FIXME: these can have very surprising behaviour where optimization levels or other
- // compilation choices change the runtime behaviour of the match.
- // See https://github.com/rust-lang/rust/issues/70861 for examples.
- ty::FnPtr(..) | ty::RawPtr(..) => {
- if !self.saw_const_match_error.get()
- && !self.saw_const_match_lint.get()
- {
- self.saw_const_match_lint.set(true);
- tcx.emit_spanned_lint(
- lint::builtin::POINTER_STRUCTURAL_MATCH,
- id,
- span,
- PointerPattern
- );
- }
- return Err(FallbackToConstRef);
- }
+ ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::FnDef(..) => PatKind::Constant {
+ value: mir::ConstantKind::Ty(ty::Const::new_value(tcx, cv, ty)),
+ },
+ ty::FnPtr(..) | ty::RawPtr(..) => unreachable!(),
_ => {
self.saw_const_match_error.set(true);
- let err = InvalidPattern { span, non_sm_ty: cv.ty() };
- tcx.sess.emit_err(err);
+ let err = InvalidPattern { span, non_sm_ty: ty };
+ tcx.sess.emit_err(err);
PatKind::Wild
}
};
@@ -472,7 +460,7 @@ impl<'tcx> ConstToPat<'tcx> {
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
- && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
+ && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, ty)
{
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
@@ -483,6 +471,6 @@ impl<'tcx> ConstToPat<'tcx> {
);
}
- Ok(Box::new(Pat { span, ty: cv.ty(), kind }))
+ Ok(Box::new(Pat { span, ty, kind }))
}
}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index 6a7714613..9df6d2f43 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -53,11 +53,11 @@ use smallvec::{smallvec, SmallVec};
use rustc_data_structures::captures::Captures;
use rustc_hir::{HirId, RangeEnd};
use rustc_index::Idx;
+use rustc_middle::middle::stability::EvalResult;
use rustc_middle::mir;
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
-use rustc_middle::{middle::stability::EvalResult, mir::interpret::ConstValue};
use rustc_session::lint;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT};
@@ -140,28 +140,17 @@ impl IntRange {
value: mir::ConstantKind<'tcx>,
) -> Option<IntRange> {
let ty = value.ty();
- if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
- let val = if let mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) = value {
- // For this specific pattern we can skip a lot of effort and go
- // straight to the result, after doing a bit of checking. (We
- // could remove this branch and just fall through, which
- // is more general but much slower.)
- scalar.to_bits_or_ptr_internal(target_size).unwrap().left()?
- } else {
- if let mir::ConstantKind::Ty(c) = value
- && let ty::ConstKind::Value(_) = c.kind()
- {
- bug!("encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val");
- }
+ let (target_size, bias) = Self::integral_size_and_signed_bias(tcx, ty)?;
+ let val = match value {
+ mir::ConstantKind::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
+ valtree.unwrap_leaf().to_bits(target_size).ok()
+ },
+ // This is a more general form of the previous case.
+ _ => value.try_eval_bits(tcx, param_env, ty),
+ }?;
- // This is a more general form of the previous case.
- value.try_eval_bits(tcx, param_env, ty)?
- };
- let val = val ^ bias;
- Some(IntRange { range: val..=val, bias })
- } else {
- None
- }
+ let val = val ^ bias;
+ Some(IntRange { range: val..=val, bias })
}
#[inline]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 1cf2f7ec0..600995927 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -18,14 +18,15 @@ use rustc_hir::pat_util::EnumerateAndAdjustIterator;
use rustc_hir::RangeEnd;
use rustc_index::Idx;
use rustc_middle::mir::interpret::{
- ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar,
+ ConstValue, ErrorHandled, GlobalId, LitToConstError, LitToConstInput, Scalar,
};
-use rustc_middle::mir::{self, UserTypeProjection};
+use rustc_middle::mir::{self, ConstantKind, UserTypeProjection};
use rustc_middle::mir::{BorrowKind, Mutability};
use rustc_middle::thir::{Ascription, BindingMode, FieldPat, LocalVarId, Pat, PatKind, PatRange};
use rustc_middle::ty::subst::{GenericArg, SubstsRef};
use rustc_middle::ty::CanonicalUserTypeAnnotation;
-use rustc_middle::ty::{self, AdtDef, ConstKind, Region, Ty, TyCtxt, UserType};
+use rustc_middle::ty::TypeVisitableExt;
+use rustc_middle::ty::{self, AdtDef, Region, Ty, TyCtxt, UserType};
use rustc_span::{Span, Symbol};
use rustc_target::abi::FieldIdx;
@@ -286,7 +287,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
ty::BindByValue(mutbl) => (mutbl, BindingMode::ByValue),
ty::BindByReference(hir::Mutability::Mut) => (
Mutability::Not,
- BindingMode::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ BindingMode::ByRef(BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
),
ty::BindByReference(hir::Mutability::Not) => {
(Mutability::Not, BindingMode::ByRef(BorrowKind::Shared))
@@ -518,16 +519,24 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
}
};
- // `mir_const_qualif` must be called with the `DefId` of the item where the const is
- // defined, not where it is declared. The difference is significant for associated
- // constants.
- let mir_structural_match_violation = self.tcx.mir_const_qualif(instance.def_id()).custom_eq;
- debug!("mir_structural_match_violation({:?}) -> {}", qpath, mir_structural_match_violation);
-
- match self.tcx.const_eval_instance(param_env_reveal_all, instance, Some(span)) {
- Ok(literal) => {
- let const_ = mir::ConstantKind::Val(literal, ty);
- let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
+ let cid = GlobalId { instance, promoted: None };
+ // Prefer valtrees over opaque constants.
+ let const_value = self
+ .tcx
+ .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span))
+ .map(|val| match val {
+ Some(valtree) => mir::ConstantKind::Ty(ty::Const::new_value(self.tcx, valtree, ty)),
+ None => mir::ConstantKind::Val(
+ self.tcx
+ .const_eval_global_id(param_env_reveal_all, cid, Some(span))
+ .expect("const_eval_global_id_for_typeck should have already failed"),
+ ty,
+ ),
+ });
+
+ match const_value {
+ Ok(const_) => {
+ let pattern = self.const_to_pat(const_, id, span, Some(instance.def_id()));
if !is_associated_const {
return pattern;
@@ -573,31 +582,72 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// Converts inline const patterns.
fn lower_inline_const(
&mut self,
- anon_const: &'tcx hir::AnonConst,
+ block: &'tcx hir::ConstBlock,
id: hir::HirId,
span: Span,
) -> PatKind<'tcx> {
- let value = mir::ConstantKind::from_inline_const(self.tcx, anon_const.def_id);
-
- // Evaluate early like we do in `lower_path`.
- let value = value.eval(self.tcx, self.param_env);
-
- match value {
- mir::ConstantKind::Ty(c) => match c.kind() {
- ConstKind::Param(_) => {
- self.tcx.sess.emit_err(ConstParamInPattern { span });
- return PatKind::Wild;
+ let tcx = self.tcx;
+ let def_id = block.def_id;
+ let body_id = block.body;
+ let expr = &tcx.hir().body(body_id).value;
+ let ty = tcx.typeck(def_id).node_type(block.hir_id);
+
+ // Special case inline consts that are just literals. This is solely
+ // a performance optimization, as we could also just go through the regular
+ // const eval path below.
+ // FIXME: investigate the performance impact of removing this.
+ let lit_input = match expr.kind {
+ hir::ExprKind::Lit(ref lit) => Some(LitToConstInput { lit: &lit.node, ty, neg: false }),
+ hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => match expr.kind {
+ hir::ExprKind::Lit(ref lit) => {
+ Some(LitToConstInput { lit: &lit.node, ty, neg: true })
}
- ConstKind::Error(_) => {
- return PatKind::Wild;
- }
- _ => bug!("Expected ConstKind::Param"),
+ _ => None,
},
- mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
- mir::ConstantKind::Unevaluated(..) => {
- // If we land here it means the const can't be evaluated because it's `TooGeneric`.
- self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
- return PatKind::Wild;
+ _ => None,
+ };
+ if let Some(lit_input) = lit_input {
+ match tcx.at(expr.span).lit_to_const(lit_input) {
+ Ok(c) => return self.const_to_pat(ConstantKind::Ty(c), id, span, None).kind,
+ // If an error occurred, ignore that it's a literal
+ // and leave reporting the error up to const eval of
+ // the unevaluated constant below.
+ Err(_) => {}
+ }
+ }
+
+ let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id());
+ let parent_substs =
+ tcx.erase_regions(ty::InternalSubsts::identity_for_item(tcx, typeck_root_def_id));
+ let substs =
+ ty::InlineConstSubsts::new(tcx, ty::InlineConstSubstsParts { parent_substs, ty })
+ .substs;
+
+ let uneval = mir::UnevaluatedConst { def: def_id.to_def_id(), substs, promoted: None };
+ debug_assert!(!substs.has_free_regions());
+
+ let ct = ty::UnevaluatedConst { def: def_id.to_def_id(), substs: substs };
+ // First try using a valtree in order to destructure the constant into a pattern.
+ if let Ok(Some(valtree)) =
+ self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
+ {
+ self.const_to_pat(
+ ConstantKind::Ty(ty::Const::new_value(self.tcx, valtree, ty)),
+ id,
+ span,
+ None,
+ )
+ .kind
+ } else {
+ // If that fails, convert it to an opaque constant pattern.
+ match tcx.const_eval_resolve(self.param_env, uneval, None) {
+ Ok(val) => self.const_to_pat(mir::ConstantKind::Val(val, ty), id, span, None).kind,
+ Err(ErrorHandled::TooGeneric) => {
+ // If we land here it means the const can't be evaluated because it's `TooGeneric`.
+ self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
+ PatKind::Wild
+ }
+ Err(ErrorHandled::Reported(_)) => PatKind::Wild,
}
}
}
@@ -626,8 +676,10 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
let lit_input =
LitToConstInput { lit: &lit.node, ty: self.typeck_results.expr_ty(expr), neg };
- match self.tcx.at(expr.span).lit_to_mir_constant(lit_input) {
- Ok(constant) => self.const_to_pat(constant, expr.hir_id, lit.span, false).kind,
+ match self.tcx.at(expr.span).lit_to_const(lit_input) {
+ Ok(constant) => {
+ self.const_to_pat(ConstantKind::Ty(constant), expr.hir_id, lit.span, None).kind
+ }
Err(LitToConstError::Reported(_)) => PatKind::Wild,
Err(LitToConstError::TypeError) => bug!("lower_lit: had type error"),
}
@@ -806,6 +858,9 @@ pub(crate) fn compare_const_vals<'tcx>(
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(a)), _a_ty),
mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(b)), _b_ty),
) => return Some(a.cmp(&b)),
+ (mir::ConstantKind::Ty(a), mir::ConstantKind::Ty(b)) => {
+ return Some(a.kind().cmp(&b.kind()));
+ }
_ => {}
},
}
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index b2f2a64e2..8d7c624a8 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -301,7 +301,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
self.print_expr(*source, depth_lvl + 2);
print_indented!(self, "}", depth_lvl);
}
- Pointer { cast, source } => {
+ PointerCoercion { cast, source } => {
print_indented!(self, "Pointer {", depth_lvl);
print_indented!(self, format!("cast: {:?}", cast), depth_lvl + 1);
print_indented!(self, "source:", depth_lvl + 1);
@@ -421,6 +421,12 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, "}", depth_lvl);
}
+ Become { value } => {
+ print_indented!(self, "Become {", depth_lvl);
+ print_indented!(self, "value:", depth_lvl + 1);
+ self.print_expr(*value, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
ConstBlock { did, substs } => {
print_indented!(self, "ConstBlock {", depth_lvl);
print_indented!(self, format!("did: {:?}", did), depth_lvl + 1);
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index d615c83d6..0540a5e94 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -409,14 +409,21 @@ where
self.drop_ladder(fields, succ, unwind).0
}
+ /// Drops the T contained in a `Box<T>` if it has not been moved out of
#[instrument(level = "debug", ret)]
- fn open_drop_for_box(&mut self, adt: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>) -> BasicBlock {
+ fn open_drop_for_box_contents(
+ &mut self,
+ adt: ty::AdtDef<'tcx>,
+ substs: SubstsRef<'tcx>,
+ succ: BasicBlock,
+ unwind: Unwind,
+ ) -> BasicBlock {
// drop glue is sent straight to codegen
// box cannot be directly dereferenced
let unique_ty = adt.non_enum_variant().fields[FieldIdx::new(0)].ty(self.tcx(), substs);
let unique_variant = unique_ty.ty_adt_def().unwrap().non_enum_variant();
let nonnull_ty = unique_variant.fields[FieldIdx::from_u32(0)].ty(self.tcx(), substs);
- let ptr_ty = self.tcx().mk_imm_ptr(substs[0].expect_ty());
+ let ptr_ty = Ty::new_imm_ptr(self.tcx(), substs[0].expect_ty());
let unique_place = self.tcx().mk_place_field(self.place, FieldIdx::new(0), unique_ty);
let nonnull_place = self.tcx().mk_place_field(unique_place, FieldIdx::new(0), nonnull_ty);
@@ -425,11 +432,7 @@ where
let interior_path = self.elaborator.deref_subpath(self.path);
- let succ = self.box_free_block(adt, substs, self.succ, self.unwind);
- let unwind_succ =
- self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup));
-
- self.drop_subpath(interior, interior_path, succ, unwind_succ)
+ self.drop_subpath(interior, interior_path, succ, unwind)
}
#[instrument(level = "debug", ret)]
@@ -453,7 +456,15 @@ where
self.open_drop_for_adt_contents(adt, substs)
};
- if adt.has_dtor(self.tcx()) {
+ if adt.is_box() {
+ // we need to drop the inside of the box before running the destructor
+ let succ = self.destructor_call_block(contents_drop);
+ let unwind = contents_drop
+ .1
+ .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup)));
+
+ self.open_drop_for_box_contents(adt, substs, succ, unwind)
+ } else if adt.has_dtor(self.tcx()) {
self.destructor_call_block(contents_drop)
} else {
contents_drop.0
@@ -617,17 +628,20 @@ where
let drop_fn = tcx.associated_item_def_ids(drop_trait)[0];
let ty = self.place_ty(self.place);
- let ref_ty =
- tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut });
+ let ref_ty = Ty::new_ref(
+ tcx,
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut },
+ );
let ref_place = self.new_temp(ref_ty);
- let unit_temp = Place::from(self.new_temp(tcx.mk_unit()));
+ let unit_temp = Place::from(self.new_temp(Ty::new_unit(tcx)));
let result = BasicBlockData {
statements: vec![self.assign(
Place::from(ref_place),
Rvalue::Ref(
tcx.lifetimes.re_erased,
- BorrowKind::Mut { allow_two_phase_borrow: false },
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
self.place,
),
)],
@@ -643,14 +657,20 @@ where
destination: unit_temp,
target: Some(succ),
unwind: unwind.into_action(),
- from_hir_call: true,
+ call_source: CallSource::Misc,
fn_span: self.source_info.span,
},
source_info: self.source_info,
}),
is_cleanup: unwind.is_cleanup(),
};
- self.elaborator.patch().new_block(result)
+
+ let destructor_block = self.elaborator.patch().new_block(result);
+
+ let block_start = Location { block: destructor_block, statement_index: 0 };
+ self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
+
+ self.drop_flag_test_block(destructor_block, succ, unwind)
}
/// Create a loop that drops an array:
@@ -676,7 +696,7 @@ where
let move_ = |place: Place<'tcx>| Operand::Move(place);
let tcx = self.tcx();
- let ptr_ty = tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut });
+ let ptr_ty = Ty::new_ptr(tcx, ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::Mut });
let ptr = Place::from(self.new_temp(ptr_ty));
let can_go = Place::from(self.new_temp(tcx.types.bool));
let one = self.constant_usize(1);
@@ -851,13 +871,7 @@ where
self.open_drop_for_tuple(&tys)
}
ty::Tuple(fields) => self.open_drop_for_tuple(fields),
- ty::Adt(def, substs) => {
- if def.is_box() {
- self.open_drop_for_box(*def, substs)
- } else {
- self.open_drop_for_adt(*def, substs)
- }
- }
+ ty::Adt(def, substs) => self.open_drop_for_adt(*def, substs),
ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
ty::Array(ety, size) => {
let size = size.try_eval_target_usize(self.tcx(), self.elaborator.param_env());
@@ -905,65 +919,6 @@ where
blk
}
- /// Creates a block that frees the backing memory of a `Box` if its drop is required (either
- /// statically or by checking its drop flag).
- ///
- /// The contained value will not be dropped.
- fn box_free_block(
- &mut self,
- adt: ty::AdtDef<'tcx>,
- substs: SubstsRef<'tcx>,
- target: BasicBlock,
- unwind: Unwind,
- ) -> BasicBlock {
- let block = self.unelaborated_free_block(adt, substs, target, unwind);
- self.drop_flag_test_block(block, target, unwind)
- }
-
- /// Creates a block that frees the backing memory of a `Box` (without dropping the contained
- /// value).
- fn unelaborated_free_block(
- &mut self,
- adt: ty::AdtDef<'tcx>,
- substs: SubstsRef<'tcx>,
- target: BasicBlock,
- unwind: Unwind,
- ) -> BasicBlock {
- let tcx = self.tcx();
- let unit_temp = Place::from(self.new_temp(tcx.mk_unit()));
- let free_func = tcx.require_lang_item(LangItem::BoxFree, Some(self.source_info.span));
- let args = adt
- .variant(FIRST_VARIANT)
- .fields
- .iter()
- .enumerate()
- .map(|(i, f)| {
- let field = FieldIdx::new(i);
- let field_ty = f.ty(tcx, substs);
- Operand::Move(tcx.mk_place_field(self.place, field, field_ty))
- })
- .collect();
-
- let call = TerminatorKind::Call {
- func: Operand::function_handle(tcx, free_func, substs, self.source_info.span),
- args,
- destination: unit_temp,
- target: Some(target),
- unwind: if unwind.is_cleanup() {
- UnwindAction::Terminate
- } else {
- UnwindAction::Continue
- },
- from_hir_call: false,
- fn_span: self.source_info.span,
- }; // FIXME(#43234)
- let free_block = self.new_block(unwind, call);
-
- let block_start = Location { block: free_block, statement_index: 0 };
- self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow);
- free_block
- }
-
fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
let block = TerminatorKind::Drop {
place: self.place,
diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
index f3b5544aa..c978bddfe 100644
--- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
@@ -1,18 +1,60 @@
//! Random access inspection of the results of a dataflow analysis.
-use crate::framework::BitSetExt;
+use crate::{framework::BitSetExt, CloneAnalysis};
-use std::borrow::Borrow;
+use std::borrow::{Borrow, BorrowMut};
use std::cmp::Ordering;
#[cfg(debug_assertions)]
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::{self, BasicBlock, Location};
-use super::{Analysis, Direction, Effect, EffectIndex, Results};
+use super::{Analysis, Direction, Effect, EffectIndex, EntrySets, Results, ResultsCloned};
+
+// `AnalysisResults` is needed as an impl such as the following has an unconstrained type
+// parameter:
+// ```
+// impl<'tcx, A, E, R> ResultsCursor<'_, 'tcx, A, R>
+// where
+// A: Analysis<'tcx>,
+// E: Borrow<EntrySets<'tcx, A>>,
+// R: Results<'tcx, A, E>,
+// {}
+// ```
+
+/// A type representing the analysis results consumed by a `ResultsCursor`.
+pub trait AnalysisResults<'tcx, A>: BorrowMut<Results<'tcx, A, Self::EntrySets>>
+where
+ A: Analysis<'tcx>,
+{
+ /// The type containing the entry sets for this `Results` type.
+ ///
+ /// Should be either `EntrySets<'tcx, A>` or `&EntrySets<'tcx, A>`.
+ type EntrySets: Borrow<EntrySets<'tcx, A>>;
+}
+impl<'tcx, A, E> AnalysisResults<'tcx, A> for Results<'tcx, A, E>
+where
+ A: Analysis<'tcx>,
+ E: Borrow<EntrySets<'tcx, A>>,
+{
+ type EntrySets = E;
+}
+impl<'a, 'tcx, A, E> AnalysisResults<'tcx, A> for &'a mut Results<'tcx, A, E>
+where
+ A: Analysis<'tcx>,
+ E: Borrow<EntrySets<'tcx, A>>,
+{
+ type EntrySets = E;
+}
/// A `ResultsCursor` that borrows the underlying `Results`.
-pub type ResultsRefCursor<'a, 'mir, 'tcx, A> = ResultsCursor<'mir, 'tcx, A, &'a Results<'tcx, A>>;
+pub type ResultsRefCursor<'res, 'mir, 'tcx, A> =
+ ResultsCursor<'mir, 'tcx, A, &'res mut Results<'tcx, A>>;
+
+/// A `ResultsCursor` which uses a cloned `Analysis` while borrowing the underlying `Results`. This
+/// allows multiple cursors over the same `Results`.
+pub type ResultsClonedCursor<'res, 'mir, 'tcx, A> =
+ ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>;
/// Allows random access inspection of the results of a dataflow analysis.
///
@@ -45,7 +87,38 @@ where
impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: Analysis<'tcx>,
- R: Borrow<Results<'tcx, A>>,
+{
+ /// Returns the dataflow state at the current location.
+ pub fn get(&self) -> &A::Domain {
+ &self.state
+ }
+
+ /// Returns the body this analysis was run on.
+ pub fn body(&self) -> &'mir mir::Body<'tcx> {
+ self.body
+ }
+
+ /// Unwraps this cursor, returning the underlying `Results`.
+ pub fn into_results(self) -> R {
+ self.results
+ }
+}
+
+impl<'res, 'mir, 'tcx, A> ResultsCursor<'mir, 'tcx, A, ResultsCloned<'res, 'tcx, A>>
+where
+ A: Analysis<'tcx> + CloneAnalysis,
+{
+ /// Creates a new cursor over the same `Results`. Note that the cursor's position is *not*
+ /// copied.
+ pub fn new_cursor(&self) -> Self {
+ Self::new(self.body, self.results.reclone_analysis())
+ }
+}
+
+impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
+where
+ A: Analysis<'tcx>,
+ R: AnalysisResults<'tcx, A>,
{
/// Returns a new cursor that can inspect `results`.
pub fn new(body: &'mir mir::Body<'tcx>, results: R) -> Self {
@@ -74,8 +147,13 @@ where
}
/// Returns the underlying `Results`.
- pub fn results(&self) -> &Results<'tcx, A> {
- &self.results.borrow()
+ pub fn results(&mut self) -> &Results<'tcx, A, R::EntrySets> {
+ self.results.borrow()
+ }
+
+ /// Returns the underlying `Results`.
+ pub fn mut_results(&mut self) -> &mut Results<'tcx, A, R::EntrySets> {
+ self.results.borrow_mut()
}
/// Returns the `Analysis` used to generate the underlying `Results`.
@@ -83,9 +161,14 @@ where
&self.results.borrow().analysis
}
- /// Returns the dataflow state at the current location.
- pub fn get(&self) -> &A::Domain {
- &self.state
+ /// Returns the `Analysis` used to generate the underlying `Results`.
+ pub fn mut_analysis(&mut self) -> &mut A {
+ &mut self.results.borrow_mut().analysis
+ }
+
+ /// Returns both the dataflow state at the current location and the `Analysis`.
+ pub fn get_with_analysis(&mut self) -> (&A::Domain, &mut A) {
+ (&self.state, &mut self.results.borrow_mut().analysis)
}
/// Resets the cursor to hold the entry set for the given basic block.
@@ -97,7 +180,7 @@ where
#[cfg(debug_assertions)]
assert!(self.reachable_blocks.contains(block));
- self.state.clone_from(&self.results.borrow().entry_set_for_block(block));
+ self.state.clone_from(self.results.borrow().entry_set_for_block(block));
self.pos = CursorPosition::block_entry(block);
self.state_needs_reset = false;
}
@@ -186,7 +269,7 @@ where
)
};
- let analysis = &self.results.borrow().analysis;
+ let analysis = &mut self.results.borrow_mut().analysis;
let target_effect_index = effect.at_index(target.statement_index);
A::Direction::apply_effects_in_range(
@@ -205,8 +288,8 @@ where
///
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
- pub fn apply_custom_effect(&mut self, f: impl FnOnce(&A, &mut A::Domain)) {
- f(&self.results.borrow().analysis, &mut self.state);
+ pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
+ f(&mut self.results.borrow_mut().analysis, &mut self.state);
self.state_needs_reset = true;
}
}
@@ -215,7 +298,6 @@ impl<'mir, 'tcx, A, R> ResultsCursor<'mir, 'tcx, A, R>
where
A: crate::GenKillAnalysis<'tcx>,
A::Domain: BitSetExt<A::Idx>,
- R: Borrow<Results<'tcx, A>>,
{
pub fn contains(&self, elem: A::Idx) -> bool {
self.get().contains(elem)
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index ba328e780..804b44a6b 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -16,7 +16,7 @@ pub trait Direction {
///
/// `effects.start()` must precede or equal `effects.end()` in this direction.
fn apply_effects_in_range<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -25,7 +25,7 @@ pub trait Direction {
A: Analysis<'tcx>;
fn apply_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -33,7 +33,7 @@ pub trait Direction {
A: Analysis<'tcx>;
fn gen_kill_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -44,13 +44,13 @@ pub trait Direction {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &R,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
+ results: &mut R,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>;
fn join_state_into_successors_of<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
@@ -67,7 +67,7 @@ impl Direction for Backward {
const IS_FORWARD: bool = false;
fn apply_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -87,7 +87,7 @@ impl Direction for Backward {
}
fn gen_kill_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -107,7 +107,7 @@ impl Direction for Backward {
}
fn apply_effects_in_range<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -187,36 +187,36 @@ impl Direction for Backward {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &R,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
+ results: &mut R,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>,
{
results.reset_to_block_entry(state, block);
- vis.visit_block_end(&state, block_data, block);
+ vis.visit_block_end(results, &state, block_data, block);
// Terminator
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.reconstruct_before_terminator_effect(state, term, loc);
- vis.visit_terminator_before_primary_effect(state, term, loc);
+ vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.reconstruct_terminator_effect(state, term, loc);
- vis.visit_terminator_after_primary_effect(state, term, loc);
+ vis.visit_terminator_after_primary_effect(results, state, term, loc);
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
let loc = Location { block, statement_index };
results.reconstruct_before_statement_effect(state, stmt, loc);
- vis.visit_statement_before_primary_effect(state, stmt, loc);
+ vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.reconstruct_statement_effect(state, stmt, loc);
- vis.visit_statement_after_primary_effect(state, stmt, loc);
+ vis.visit_statement_after_primary_effect(results, state, stmt, loc);
}
- vis.visit_block_start(state, block_data, block);
+ vis.visit_block_start(results, state, block_data, block);
}
fn join_state_into_successors_of<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
_tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
@@ -319,7 +319,7 @@ impl Direction for Forward {
const IS_FORWARD: bool = true;
fn apply_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -339,7 +339,7 @@ impl Direction for Forward {
}
fn gen_kill_effects_in_block<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
trans: &mut GenKillSet<A::Idx>,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -359,7 +359,7 @@ impl Direction for Forward {
}
fn apply_effects_in_range<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
state: &mut A::Domain,
block: BasicBlock,
block_data: &mir::BasicBlockData<'tcx>,
@@ -435,35 +435,35 @@ impl Direction for Forward {
state: &mut F,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &R,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
+ results: &mut R,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
R: ResultsVisitable<'tcx, FlowState = F>,
{
results.reset_to_block_entry(state, block);
- vis.visit_block_start(state, block_data, block);
+ vis.visit_block_start(results, state, block_data, block);
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
results.reconstruct_before_statement_effect(state, stmt, loc);
- vis.visit_statement_before_primary_effect(state, stmt, loc);
+ vis.visit_statement_before_primary_effect(results, state, stmt, loc);
results.reconstruct_statement_effect(state, stmt, loc);
- vis.visit_statement_after_primary_effect(state, stmt, loc);
+ vis.visit_statement_after_primary_effect(results, state, stmt, loc);
}
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
results.reconstruct_before_terminator_effect(state, term, loc);
- vis.visit_terminator_before_primary_effect(state, term, loc);
+ vis.visit_terminator_before_primary_effect(results, state, term, loc);
results.reconstruct_terminator_effect(state, term, loc);
- vis.visit_terminator_after_primary_effect(state, term, loc);
+ vis.visit_terminator_after_primary_effect(results, state, term, loc);
- vis.visit_block_end(state, block_data, block);
+ vis.visit_block_end(results, state, block_data, block);
}
fn join_state_into_successors_of<'tcx, A>(
- analysis: &A,
+ analysis: &mut A,
_tcx: TyCtxt<'tcx>,
_body: &mir::Body<'tcx>,
exit_state: &mut A::Domain,
@@ -502,15 +502,7 @@ impl Direction for Forward {
propagate(target, exit_state);
}
- Call {
- unwind,
- destination,
- target,
- func: _,
- args: _,
- from_hir_call: _,
- fn_span: _,
- } => {
+ Call { unwind, destination, target, func: _, args: _, call_source: _, fn_span: _ } => {
if let UnwindAction::Cleanup(unwind) = unwind {
propagate(unwind, exit_state);
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index 3e8f792e6..c755d7588 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -5,7 +5,9 @@ use crate::errors::{
};
use crate::framework::BitSetExt;
+use std::borrow::Borrow;
use std::ffi::OsString;
+use std::marker::PhantomData;
use std::path::PathBuf;
use rustc_ast as ast;
@@ -22,54 +24,108 @@ use rustc_span::symbol::{sym, Symbol};
use super::fmt::DebugWithContext;
use super::graphviz;
use super::{
- visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice,
- ResultsCursor, ResultsVisitor,
+ visit_results, Analysis, AnalysisDomain, CloneAnalysis, Direction, GenKill, GenKillAnalysis,
+ GenKillSet, JoinSemiLattice, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
+ ResultsVisitor,
};
+pub type EntrySets<'tcx, A> = IndexVec<BasicBlock, <A as AnalysisDomain<'tcx>>::Domain>;
+
/// A dataflow analysis that has converged to fixpoint.
-pub struct Results<'tcx, A>
+pub struct Results<'tcx, A, E = EntrySets<'tcx, A>>
where
A: Analysis<'tcx>,
{
pub analysis: A,
- pub(super) entry_sets: IndexVec<BasicBlock, A::Domain>,
+ pub(super) entry_sets: E,
+ pub(super) _marker: PhantomData<&'tcx ()>,
}
-impl<'tcx, A> Results<'tcx, A>
+/// `Results` type with a cloned `Analysis` and borrowed entry sets.
+pub type ResultsCloned<'res, 'tcx, A> = Results<'tcx, A, &'res EntrySets<'tcx, A>>;
+
+impl<'tcx, A, E> Results<'tcx, A, E>
where
A: Analysis<'tcx>,
+ E: Borrow<EntrySets<'tcx, A>>,
{
/// Creates a `ResultsCursor` that can inspect these `Results`.
pub fn into_results_cursor<'mir>(
self,
body: &'mir mir::Body<'tcx>,
- ) -> ResultsCursor<'mir, 'tcx, A> {
+ ) -> ResultsCursor<'mir, 'tcx, A, Self> {
ResultsCursor::new(body, self)
}
/// Gets the dataflow state for the given block.
pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
- &self.entry_sets[block]
+ &self.entry_sets.borrow()[block]
}
pub fn visit_with<'mir>(
- &self,
+ &mut self,
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
) {
visit_results(body, blocks, self, vis)
}
pub fn visit_reachable_with<'mir>(
- &self,
+ &mut self,
body: &'mir mir::Body<'tcx>,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, Self, FlowState = A::Domain>,
) {
let blocks = mir::traversal::reachable(body);
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
}
}
+impl<'tcx, A> Results<'tcx, A>
+where
+ A: Analysis<'tcx>,
+{
+ /// Creates a `ResultsCursor` that can inspect these `Results`.
+ pub fn as_results_cursor<'a, 'mir>(
+ &'a mut self,
+ body: &'mir mir::Body<'tcx>,
+ ) -> ResultsRefCursor<'a, 'mir, 'tcx, A> {
+ ResultsCursor::new(body, self)
+ }
+}
+impl<'tcx, A> Results<'tcx, A>
+where
+ A: Analysis<'tcx> + CloneAnalysis,
+{
+ /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
+ pub fn clone_analysis(&self) -> ResultsCloned<'_, 'tcx, A> {
+ Results {
+ analysis: self.analysis.clone_analysis(),
+ entry_sets: &self.entry_sets,
+ _marker: PhantomData,
+ }
+ }
+
+ /// Creates a `ResultsCursor` that can inspect these `Results`.
+ pub fn cloned_results_cursor<'mir>(
+ &self,
+ body: &'mir mir::Body<'tcx>,
+ ) -> ResultsClonedCursor<'_, 'mir, 'tcx, A> {
+ self.clone_analysis().into_results_cursor(body)
+ }
+}
+impl<'res, 'tcx, A> Results<'tcx, A, &'res EntrySets<'tcx, A>>
+where
+ A: Analysis<'tcx> + CloneAnalysis,
+{
+ /// Creates a new `Results` type with a cloned `Analysis` and borrowed entry sets.
+ pub fn reclone_analysis(&self) -> Self {
+ Results {
+ analysis: self.analysis.clone_analysis(),
+ entry_sets: self.entry_sets,
+ _marker: PhantomData,
+ }
+ }
+}
/// A solver for dataflow problems.
pub struct Engine<'a, 'tcx, A>
@@ -98,7 +154,7 @@ 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>, analysis: A) -> Self {
+ pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, mut 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).
//
@@ -114,7 +170,7 @@ where
for (block, block_data) in body.basic_blocks.iter_enumerated() {
let trans = &mut trans_for_block[block];
- A::Direction::gen_kill_effects_in_block(&analysis, trans, block, block_data);
+ A::Direction::gen_kill_effects_in_block(&mut analysis, trans, block, block_data);
}
let apply_trans = Box::new(move |bb: BasicBlock, state: &mut A::Domain| {
@@ -171,7 +227,13 @@ where
A::Domain: DebugWithContext<A>,
{
let Engine {
- analysis, body, mut entry_sets, tcx, apply_trans_for_block, pass_name, ..
+ mut analysis,
+ body,
+ mut entry_sets,
+ tcx,
+ apply_trans_for_block,
+ pass_name,
+ ..
} = self;
let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body.basic_blocks.len());
@@ -203,11 +265,13 @@ where
// 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),
+ None => {
+ A::Direction::apply_effects_in_block(&mut analysis, &mut state, bb, bb_data)
+ }
}
A::Direction::join_state_into_successors_of(
- &analysis,
+ &mut analysis,
tcx,
body,
&mut state,
@@ -221,11 +285,13 @@ where
);
}
- let results = Results { analysis, entry_sets };
+ let mut results = Results { analysis, entry_sets, _marker: PhantomData };
- let res = write_graphviz_results(tcx, &body, &results, pass_name);
- if let Err(e) = res {
- error!("Failed to write graphviz dataflow results: {}", e);
+ if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
+ let res = write_graphviz_results(tcx, &body, &mut results, pass_name);
+ if let Err(e) = res {
+ error!("Failed to write graphviz dataflow results: {}", e);
+ }
}
results
@@ -235,11 +301,11 @@ where
// Graphviz
/// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
-/// `rustc_mir` attributes.
+/// `rustc_mir` attributes and `-Z dump-mir-dataflow`.
fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &mir::Body<'tcx>,
- results: &Results<'tcx, A>,
+ results: &mut Results<'tcx, A>,
pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
@@ -264,9 +330,7 @@ where
io::BufWriter::new(fs::File::create(&path)?)
}
- None if tcx.sess.opts.unstable_opts.dump_mir_dataflow
- && dump_enabled(tcx, A::NAME, def_id) =>
- {
+ None if dump_enabled(tcx, A::NAME, def_id) => {
create_dump_file(tcx, ".dot", false, A::NAME, &pass_name.unwrap_or("-----"), body)?
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index 707729f8f..e331533c3 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -1,6 +1,7 @@
//! A helpful diagram for debugging dataflow problems.
use std::borrow::Cow;
+use std::cell::RefCell;
use std::sync::OnceLock;
use std::{io, ops, str};
@@ -28,23 +29,27 @@ impl OutputStyle {
}
}
-pub struct Formatter<'a, 'tcx, A>
+pub struct Formatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
- body: &'a Body<'tcx>,
- results: &'a Results<'tcx, A>,
+ body: &'mir Body<'tcx>,
+ results: RefCell<&'res mut Results<'tcx, A>>,
style: OutputStyle,
reachable: BitSet<BasicBlock>,
}
-impl<'a, 'tcx, A> Formatter<'a, 'tcx, A>
+impl<'res, 'mir, 'tcx, A> Formatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
- pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self {
+ pub fn new(
+ body: &'mir Body<'tcx>,
+ results: &'res mut Results<'tcx, A>,
+ style: OutputStyle,
+ ) -> Self {
let reachable = mir::traversal::reachable_as_bitset(body);
- Formatter { body, results, style, reachable }
+ Formatter { body, results: results.into(), style, reachable }
}
}
@@ -64,7 +69,7 @@ fn dataflow_successors(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
.collect()
}
-impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, 'tcx, A>
+impl<'tcx, A> dot::Labeller<'_> for Formatter<'_, '_, 'tcx, A>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@@ -83,13 +88,14 @@ where
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
let mut label = Vec::new();
+ let mut results = self.results.borrow_mut();
let mut fmt = BlockFormatter {
- results: ResultsRefCursor::new(self.body, self.results),
+ results: results.as_results_cursor(self.body),
style: self.style,
bg: Background::Light,
};
- fmt.write_node_label(&mut label, self.body, *block).unwrap();
+ fmt.write_node_label(&mut label, *block).unwrap();
dot::LabelText::html(String::from_utf8(label).unwrap())
}
@@ -103,7 +109,7 @@ where
}
}
-impl<'a, 'tcx, A> dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
+impl<'mir, 'tcx, A> dot::GraphWalk<'mir> for Formatter<'_, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
@@ -137,16 +143,16 @@ where
}
}
-struct BlockFormatter<'a, 'tcx, A>
+struct BlockFormatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
{
- results: ResultsRefCursor<'a, 'a, 'tcx, A>,
+ results: ResultsRefCursor<'res, 'mir, 'tcx, A>,
bg: Background,
style: OutputStyle,
}
-impl<'a, 'tcx, A> BlockFormatter<'a, 'tcx, A>
+impl<'res, 'mir, 'tcx, A> BlockFormatter<'res, 'mir, 'tcx, A>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@@ -159,12 +165,7 @@ where
bg
}
- fn write_node_label(
- &mut self,
- w: &mut impl io::Write,
- body: &'a Body<'tcx>,
- block: BasicBlock,
- ) -> io::Result<()> {
+ fn write_node_label(&mut self, w: &mut impl io::Write, block: BasicBlock) -> io::Result<()> {
// Sample output:
// +-+-----------------------------------------------+
// A | bb4 |
@@ -215,11 +216,11 @@ where
self.write_row_with_full_state(w, "", "(on start)")?;
// D + E: Statement and terminator transfer functions
- self.write_statements_and_terminator(w, body, block)?;
+ self.write_statements_and_terminator(w, block)?;
// F: State at end of block
- let terminator = body[block].terminator();
+ let terminator = self.results.body()[block].terminator();
// Write the full dataflow state immediately after the terminator if it differs from the
// state at block entry.
@@ -389,10 +390,14 @@ where
fn write_statements_and_terminator(
&mut self,
w: &mut impl io::Write,
- body: &'a Body<'tcx>,
block: BasicBlock,
) -> io::Result<()> {
- let diffs = StateDiffCollector::run(body, block, self.results.results(), self.style);
+ let diffs = StateDiffCollector::run(
+ self.results.body(),
+ block,
+ self.results.mut_results(),
+ self.style,
+ );
let mut diffs_before = diffs.before.map(|v| v.into_iter());
let mut diffs_after = diffs.after.into_iter();
@@ -401,7 +406,7 @@ where
if A::Direction::IS_FORWARD { it.next().unwrap() } else { it.next_back().unwrap() }
};
- for (i, statement) in body[block].statements.iter().enumerate() {
+ for (i, statement) in self.results.body()[block].statements.iter().enumerate() {
let statement_str = format!("{statement:?}");
let index_str = format!("{i}");
@@ -423,7 +428,7 @@ where
assert!(diffs_after.is_empty());
assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty));
- let terminator = body[block].terminator();
+ let terminator = self.results.body()[block].terminator();
let mut terminator_str = String::new();
terminator.kind.fmt_head(&mut terminator_str).unwrap();
@@ -492,29 +497,24 @@ where
}
}
-struct StateDiffCollector<'a, 'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- analysis: &'a A,
- prev_state: A::Domain,
+struct StateDiffCollector<D> {
+ prev_state: D,
before: Option<Vec<String>>,
after: Vec<String>,
}
-impl<'a, 'tcx, A> StateDiffCollector<'a, 'tcx, A>
-where
- A: Analysis<'tcx>,
- A::Domain: DebugWithContext<A>,
-{
- fn run(
- body: &'a mir::Body<'tcx>,
+impl<D> StateDiffCollector<D> {
+ fn run<'tcx, A>(
+ body: &mir::Body<'tcx>,
block: BasicBlock,
- results: &'a Results<'tcx, A>,
+ results: &mut Results<'tcx, A>,
style: OutputStyle,
- ) -> Self {
+ ) -> Self
+ where
+ A: Analysis<'tcx, Domain = D>,
+ D: DebugWithContext<A>,
+ {
let mut collector = StateDiffCollector {
- analysis: &results.analysis,
prev_state: results.analysis.bottom_value(body),
after: vec![],
before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
@@ -525,7 +525,7 @@ where
}
}
-impl<'a, 'tcx, A> ResultsVisitor<'a, 'tcx> for StateDiffCollector<'a, 'tcx, A>
+impl<'tcx, A> ResultsVisitor<'_, 'tcx, Results<'tcx, A>> for StateDiffCollector<A::Domain>
where
A: Analysis<'tcx>,
A::Domain: DebugWithContext<A>,
@@ -534,6 +534,7 @@ where
fn visit_block_start(
&mut self,
+ _results: &Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@@ -545,6 +546,7 @@ where
fn visit_block_end(
&mut self,
+ _results: &Results<'tcx, A>,
state: &Self::FlowState,
_block_data: &mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@@ -556,45 +558,49 @@ where
fn visit_statement_before_primary_effect(
&mut self,
+ results: &Results<'tcx, A>,
state: &Self::FlowState,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
- before.push(diff_pretty(state, &self.prev_state, self.analysis));
+ before.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}
fn visit_statement_after_primary_effect(
&mut self,
+ results: &Results<'tcx, A>,
state: &Self::FlowState,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
- self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+ self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
fn visit_terminator_before_primary_effect(
&mut self,
+ results: &Results<'tcx, A>,
state: &Self::FlowState,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
- before.push(diff_pretty(state, &self.prev_state, self.analysis));
+ before.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}
fn visit_terminator_after_primary_effect(
&mut self,
+ results: &Results<'tcx, A>,
state: &Self::FlowState,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
- self.after.push(diff_pretty(state, &self.prev_state, self.analysis));
+ self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
self.prev_state.clone_from(state)
}
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index f2263007f..58df9b9a7 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -45,9 +45,9 @@ pub mod graphviz;
pub mod lattice;
mod visitor;
-pub use self::cursor::{ResultsCursor, ResultsRefCursor};
+pub use self::cursor::{AnalysisResults, ResultsClonedCursor, ResultsCursor, ResultsRefCursor};
pub use self::direction::{Backward, Direction, Forward};
-pub use self::engine::{Engine, Results};
+pub use self::engine::{Engine, EntrySets, Results, ResultsCloned};
pub use self::lattice::{JoinSemiLattice, MeetSemiLattice};
pub use self::visitor::{visit_results, ResultsVisitable, ResultsVisitor};
@@ -146,7 +146,7 @@ pub trait AnalysisDomain<'tcx> {
pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// Updates the current dataflow state with the effect of evaluating a statement.
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -159,7 +159,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_statement_effect`.
fn apply_before_statement_effect(
- &self,
+ &mut self,
_state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -173,7 +173,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// `InitializedPlaces` analyses, the return place for a function call is not marked as
/// initialized here.
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -186,7 +186,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule,
/// analyses should not implement this without also implementing `apply_terminator_effect`.
fn apply_before_terminator_effect(
- &self,
+ &mut self,
_state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -201,7 +201,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// This is separate from `apply_terminator_effect` to properly track state across unwind
/// edges.
fn apply_call_return_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -214,7 +214,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
///
/// By default, no effects happen.
fn apply_yield_resume_effect(
- &self,
+ &mut self,
_state: &mut Self::Domain,
_resume_block: BasicBlock,
_resume_place: mir::Place<'tcx>,
@@ -235,7 +235,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
/// engine doesn't need to clone the exit state for a block unless
/// `SwitchIntEdgeEffects::apply` is actually called.
fn apply_switch_int_edge_effects(
- &self,
+ &mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
@@ -269,6 +269,21 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
}
}
+/// Defines an `Analysis` which can be cloned for use in multiple `ResultsCursor`s or
+/// `ResultsVisitor`s. Note this need not be a full clone, only enough of one to be used with a new
+/// `ResultsCursor` or `ResultsVisitor`
+pub trait CloneAnalysis {
+ fn clone_analysis(&self) -> Self;
+}
+impl<'tcx, A> CloneAnalysis for A
+where
+ A: Analysis<'tcx> + Copy,
+{
+ fn clone_analysis(&self) -> Self {
+ *self
+ }
+}
+
/// A gen/kill dataflow problem.
///
/// Each method in this trait has a corresponding one in `Analysis`. However, these methods only
@@ -282,7 +297,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_statement_effect`.
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -290,7 +305,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_before_statement_effect`.
fn before_statement_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -299,7 +314,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_terminator_effect`.
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -307,7 +322,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_before_terminator_effect`.
fn before_terminator_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -318,7 +333,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_call_return_effect`.
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -326,7 +341,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_yield_resume_effect`.
fn yield_resume_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_resume_block: BasicBlock,
_resume_place: mir::Place<'tcx>,
@@ -335,7 +350,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
/// See `Analysis::apply_switch_int_edge_effects`.
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
- &self,
+ &mut self,
_block: BasicBlock,
_discr: &mir::Operand<'tcx>,
_edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@@ -349,7 +364,7 @@ where
A::Domain: GenKill<A::Idx> + BitSetExt<A::Idx>,
{
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -358,7 +373,7 @@ where
}
fn apply_before_statement_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -367,7 +382,7 @@ where
}
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -376,7 +391,7 @@ where
}
fn apply_before_terminator_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -387,7 +402,7 @@ where
/* Edge-specific effects */
fn apply_call_return_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -396,7 +411,7 @@ where
}
fn apply_yield_resume_effect(
- &self,
+ &mut self,
state: &mut A::Domain,
resume_block: BasicBlock,
resume_place: mir::Place<'tcx>,
@@ -405,7 +420,7 @@ where
}
fn apply_switch_int_edge_effects(
- &self,
+ &mut self,
block: BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<A::Domain>,
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index 0fed305b9..cb0ec144e 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -40,7 +40,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
destination: dummy_place.clone(),
target: Some(mir::START_BLOCK),
unwind: mir::UnwindAction::Continue,
- from_hir_call: false,
+ call_source: mir::CallSource::Misc,
fn_span: DUMMY_SP,
},
);
@@ -54,7 +54,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
destination: dummy_place.clone(),
target: Some(mir::START_BLOCK),
unwind: mir::UnwindAction::Continue,
- from_hir_call: false,
+ call_source: mir::CallSource::Misc,
fn_span: DUMMY_SP,
},
);
@@ -179,7 +179,7 @@ impl<'tcx, D: Direction> AnalysisDomain<'tcx> for MockAnalysis<'tcx, D> {
impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -189,7 +189,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_before_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -199,7 +199,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -209,7 +209,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_before_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -219,7 +219,7 @@ impl<'tcx, D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
}
fn apply_call_return_effect(
- &self,
+ &mut self,
_state: &mut Self::Domain,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -266,7 +266,8 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
let body = analysis.body;
let mut cursor =
- Results { entry_sets: analysis.mock_entry_sets(), analysis }.into_results_cursor(body);
+ Results { entry_sets: analysis.mock_entry_sets(), analysis, _marker: PhantomData }
+ .into_results_cursor(body);
cursor.allow_unreachable();
diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
index 75b4e150a..76a729827 100644
--- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
@@ -1,16 +1,18 @@
+use std::borrow::Borrow;
+
use rustc_middle::mir::{self, BasicBlock, Location};
-use super::{Analysis, Direction, Results};
+use super::{Analysis, Direction, EntrySets, Results};
/// Calls the corresponding method in `ResultsVisitor` for every location in a `mir::Body` with the
/// dataflow state at that location.
-pub fn visit_results<'mir, 'tcx, F, V>(
+pub fn visit_results<'mir, 'tcx, F, R>(
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
- results: &V,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = F>,
+ results: &mut R,
+ vis: &mut impl ResultsVisitor<'mir, 'tcx, R, FlowState = F>,
) where
- V: ResultsVisitable<'tcx, FlowState = F>,
+ R: ResultsVisitable<'tcx, FlowState = F>,
{
let mut state = results.new_flow_state(body);
@@ -22,15 +24,18 @@ pub fn visit_results<'mir, 'tcx, F, V>(
assert!(reachable_blocks.contains(block));
let block_data = &body[block];
- V::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
+ R::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
}
}
-pub trait ResultsVisitor<'mir, 'tcx> {
+/// A visitor over the results of an `Analysis`. The type parameter `R` is the results type being
+/// visited.
+pub trait ResultsVisitor<'mir, 'tcx, R> {
type FlowState;
fn visit_block_start(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_block_data: &'mir mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@@ -41,6 +46,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// its `statement_effect`.
fn visit_statement_before_primary_effect(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_statement: &'mir mir::Statement<'tcx>,
_location: Location,
@@ -51,6 +57,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// statement applied to `state`.
fn visit_statement_after_primary_effect(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_statement: &'mir mir::Statement<'tcx>,
_location: Location,
@@ -61,6 +68,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// its `terminator_effect`.
fn visit_terminator_before_primary_effect(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
@@ -73,6 +81,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn visit_terminator_after_primary_effect(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_terminator: &'mir mir::Terminator<'tcx>,
_location: Location,
@@ -81,6 +90,7 @@ pub trait ResultsVisitor<'mir, 'tcx> {
fn visit_block_end(
&mut self,
+ _results: &R,
_state: &Self::FlowState,
_block_data: &'mir mir::BasicBlockData<'tcx>,
_block: BasicBlock,
@@ -105,37 +115,38 @@ pub trait ResultsVisitable<'tcx> {
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock);
fn reconstruct_before_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
statement: &mir::Statement<'tcx>,
location: Location,
);
fn reconstruct_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
statement: &mir::Statement<'tcx>,
location: Location,
);
fn reconstruct_before_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
terminator: &mir::Terminator<'tcx>,
location: Location,
);
fn reconstruct_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
terminator: &mir::Terminator<'tcx>,
location: Location,
);
}
-impl<'tcx, A> ResultsVisitable<'tcx> for Results<'tcx, A>
+impl<'tcx, A, E> ResultsVisitable<'tcx> for Results<'tcx, A, E>
where
A: Analysis<'tcx>,
+ E: Borrow<EntrySets<'tcx, A>>,
{
type FlowState = A::Domain;
@@ -146,11 +157,11 @@ where
}
fn reset_to_block_entry(&self, state: &mut Self::FlowState, block: BasicBlock) {
- state.clone_from(&self.entry_set_for_block(block));
+ state.clone_from(self.entry_set_for_block(block));
}
fn reconstruct_before_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -159,7 +170,7 @@ where
}
fn reconstruct_statement_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
stmt: &mir::Statement<'tcx>,
loc: Location,
@@ -168,7 +179,7 @@ where
}
fn reconstruct_before_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
@@ -177,7 +188,7 @@ where
}
fn reconstruct_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::FlowState,
term: &mir::Terminator<'tcx>,
loc: Location,
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index 92d30f254..b88ed32b6 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -10,6 +10,7 @@ use rustc_middle::mir::*;
/// At present, this is used as a very limited form of alias analysis. For example,
/// `MaybeBorrowedLocals` is used to compute which locals are live during a yield expression for
/// immovable generators.
+#[derive(Clone, Copy)]
pub struct MaybeBorrowedLocals;
impl MaybeBorrowedLocals {
@@ -36,7 +37,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
type Idx = Local;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -45,7 +46,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -54,7 +55,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
}
fn call_return_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 6ae6bdc17..9662c1977 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -21,6 +21,7 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi
/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
+#[derive(Clone, Copy)]
pub struct MaybeLiveLocals;
impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
@@ -43,7 +44,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
type Idx = Local;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -52,7 +53,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -61,7 +62,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -74,7 +75,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
}
fn yield_resume_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_resume_block: mir::BasicBlock,
resume_place: mir::Place<'tcx>,
@@ -215,6 +216,7 @@ impl DefUse {
/// This is basically written for dead store elimination and nothing else.
///
/// All of the caveats of `MaybeLiveLocals` apply.
+#[derive(Clone, Copy)]
pub struct MaybeTransitiveLiveLocals<'a> {
always_live: &'a BitSet<Local>,
}
@@ -247,7 +249,7 @@ impl<'a, 'tcx> AnalysisDomain<'tcx> for MaybeTransitiveLiveLocals<'a> {
impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
fn apply_statement_effect(
- &self,
+ &mut self,
trans: &mut Self::Domain,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -282,7 +284,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_terminator_effect(
- &self,
+ &mut self,
trans: &mut Self::Domain,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -291,7 +293,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_call_return_effect(
- &self,
+ &mut self,
trans: &mut Self::Domain,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -304,7 +306,7 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
}
fn apply_yield_resume_effect(
- &self,
+ &mut self,
trans: &mut Self::Domain,
_resume_block: mir::BasicBlock,
resume_place: mir::Place<'tcx>,
diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs
index 171db6965..98cec1c67 100644
--- a/compiler/rustc_mir_dataflow/src/impls/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs
@@ -306,7 +306,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
statement: &mir::Statement<'tcx>,
location: Location,
@@ -329,7 +329,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -351,7 +351,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -372,7 +372,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
- &self,
+ &mut self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@@ -442,7 +442,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -456,7 +456,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -467,7 +467,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -488,7 +488,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
- &self,
+ &mut self,
block: mir::BasicBlock,
discr: &mir::Operand<'tcx>,
edge_effects: &mut impl SwitchIntEdgeEffects<G>,
@@ -562,7 +562,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
type Idx = MovePathIndex;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_statement: &mir::Statement<'tcx>,
location: Location,
@@ -573,7 +573,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -584,7 +584,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -627,7 +627,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
#[instrument(skip(self, trans), level = "debug")]
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
location: Location,
@@ -651,7 +651,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
#[instrument(skip(self, trans, _terminator), level = "debug")]
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_terminator: &mir::Terminator<'tcx>,
location: Location,
@@ -672,7 +672,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
block: mir::BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 463ce083a..666c8d50a 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -1,10 +1,9 @@
pub use super::*;
-use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
+use crate::{CallReturnPlaces, GenKill, ResultsClonedCursor};
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use std::borrow::Cow;
-use std::cell::RefCell;
#[derive(Clone)]
pub struct MaybeStorageLive<'a> {
@@ -17,6 +16,12 @@ impl<'a> MaybeStorageLive<'a> {
}
}
+impl crate::CloneAnalysis for MaybeStorageLive<'_> {
+ fn clone_analysis(&self) -> Self {
+ self.clone()
+ }
+}
+
impl<'tcx, 'a> crate::AnalysisDomain<'tcx> for MaybeStorageLive<'a> {
type Domain = BitSet<Local>;
@@ -43,7 +48,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
type Idx = Local;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
_: Location,
@@ -56,7 +61,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
}
fn terminator_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
_: Location,
@@ -65,7 +70,7 @@ impl<'tcx, 'a> crate::GenKillAnalysis<'tcx> for MaybeStorageLive<'a> {
}
fn call_return_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -110,7 +115,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
type Idx = Local;
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
_: Location,
@@ -123,7 +128,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
fn terminator_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_: &mir::Terminator<'tcx>,
_: Location,
@@ -132,7 +137,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
fn call_return_effect(
- &self,
+ &mut self,
_trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_return_places: CallReturnPlaces<'_, 'tcx>,
@@ -141,28 +146,28 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeStorageDead {
}
}
-type BorrowedLocalsResults<'a, 'tcx> = ResultsRefCursor<'a, 'a, 'tcx, MaybeBorrowedLocals>;
+type BorrowedLocalsResults<'res, 'mir, 'tcx> =
+ ResultsClonedCursor<'res, 'mir, 'tcx, MaybeBorrowedLocals>;
/// Dataflow analysis that determines whether each local requires storage at a
/// given location; i.e. whether its storage can go away without being observed.
-pub struct MaybeRequiresStorage<'mir, 'tcx> {
- body: &'mir Body<'tcx>,
- borrowed_locals: RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
+pub struct MaybeRequiresStorage<'res, 'mir, 'tcx> {
+ borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>,
}
-impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
- pub fn new(
- body: &'mir Body<'tcx>,
- borrowed_locals: &'mir Results<'tcx, MaybeBorrowedLocals>,
- ) -> Self {
- MaybeRequiresStorage {
- body,
- borrowed_locals: RefCell::new(ResultsRefCursor::new(&body, borrowed_locals)),
- }
+impl<'res, 'mir, 'tcx> MaybeRequiresStorage<'res, 'mir, 'tcx> {
+ pub fn new(borrowed_locals: BorrowedLocalsResults<'res, 'mir, 'tcx>) -> Self {
+ MaybeRequiresStorage { borrowed_locals }
+ }
+}
+
+impl crate::CloneAnalysis for MaybeRequiresStorage<'_, '_, '_> {
+ fn clone_analysis(&self) -> Self {
+ Self { borrowed_locals: self.borrowed_locals.new_cursor() }
}
}
-impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
+impl<'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
type Domain = BitSet<Local>;
const NAME: &'static str = "requires_storage";
@@ -181,17 +186,17 @@ impl<'mir, 'tcx> crate::AnalysisDomain<'tcx> for MaybeRequiresStorage<'mir, 'tcx
}
}
-impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tcx> {
+impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, '_, 'tcx> {
type Idx = Local;
fn before_statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
stmt: &mir::Statement<'tcx>,
loc: Location,
) {
// If a place is borrowed in a statement, it needs storage for that statement.
- self.borrowed_locals.borrow().analysis().statement_effect(trans, stmt, loc);
+ self.borrowed_locals.mut_analysis().statement_effect(trans, stmt, loc);
match &stmt.kind {
StatementKind::StorageDead(l) => trans.kill(*l),
@@ -218,7 +223,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn statement_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_: &mir::Statement<'tcx>,
loc: Location,
@@ -229,13 +234,13 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn before_terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
loc: Location,
) {
// If a place is borrowed in a terminator, it needs storage for that terminator.
- self.borrowed_locals.borrow().analysis().terminator_effect(trans, terminator, loc);
+ self.borrowed_locals.mut_analysis().terminator_effect(trans, terminator, loc);
match &terminator.kind {
TerminatorKind::Call { destination, .. } => {
@@ -282,7 +287,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn terminator_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
terminator: &mir::Terminator<'tcx>,
loc: Location,
@@ -321,7 +326,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn call_return_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
return_places: CallReturnPlaces<'_, 'tcx>,
@@ -330,7 +335,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
fn yield_resume_effect(
- &self,
+ &mut self,
trans: &mut impl GenKill<Self::Idx>,
_resume_block: BasicBlock,
resume_place: mir::Place<'tcx>,
@@ -339,28 +344,28 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
}
}
-impl<'mir, 'tcx> MaybeRequiresStorage<'mir, 'tcx> {
+impl<'tcx> MaybeRequiresStorage<'_, '_, 'tcx> {
/// Kill locals that are fully moved and have not been borrowed.
- fn check_for_move(&self, trans: &mut impl GenKill<Local>, loc: Location) {
- let mut visitor = MoveVisitor { trans, borrowed_locals: &self.borrowed_locals };
- visitor.visit_location(&self.body, loc);
+ fn check_for_move(&mut self, trans: &mut impl GenKill<Local>, loc: Location) {
+ let body = self.borrowed_locals.body();
+ let mut visitor = MoveVisitor { trans, borrowed_locals: &mut self.borrowed_locals };
+ visitor.visit_location(body, loc);
}
}
-struct MoveVisitor<'a, 'mir, 'tcx, T> {
- borrowed_locals: &'a RefCell<BorrowedLocalsResults<'mir, 'tcx>>,
+struct MoveVisitor<'a, 'res, 'mir, 'tcx, T> {
+ borrowed_locals: &'a mut BorrowedLocalsResults<'res, 'mir, 'tcx>,
trans: &'a mut T,
}
-impl<'a, 'mir, 'tcx, T> Visitor<'tcx> for MoveVisitor<'a, 'mir, 'tcx, T>
+impl<'tcx, T> Visitor<'tcx> for MoveVisitor<'_, '_, '_, 'tcx, T>
where
T: GenKill<Local>,
{
fn visit_local(&mut self, local: Local, context: PlaceContext, loc: Location) {
if PlaceContext::NonMutatingUse(NonMutatingUseContext::Move) == context {
- let mut borrowed_locals = self.borrowed_locals.borrow_mut();
- borrowed_locals.seek_before_primary_effect(loc);
- if !borrowed_locals.contains(local) {
+ self.borrowed_locals.seek_before_primary_effect(loc);
+ if !self.borrowed_locals.contains(local) {
self.trans.kill(local);
}
}
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index fc4efb943..d43446bc5 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -27,9 +27,10 @@ pub use self::drop_flag_effects::{
on_lookup_result_bits,
};
pub use self::framework::{
- fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
- Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
- ResultsRefCursor, ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
+ fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, AnalysisResults, Backward,
+ CallReturnPlaces, CloneAnalysis, Direction, Engine, Forward, GenKill, GenKillAnalysis,
+ JoinSemiLattice, Results, ResultsCloned, ResultsClonedCursor, ResultsCursor, ResultsRefCursor,
+ ResultsVisitable, ResultsVisitor, SwitchIntEdgeEffects,
};
use self::move_paths::MoveData;
@@ -42,7 +43,6 @@ pub mod impls;
pub mod move_paths;
pub mod rustc_peek;
pub mod storage;
-pub mod un_derefer;
pub mod value_analysis;
fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 096bc0acf..dc7e9ab3c 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -1,11 +1,10 @@
-use crate::move_paths::FxHashMap;
-use crate::un_derefer::UnDerefer;
use rustc_index::IndexVec;
use rustc_middle::mir::tcx::RvalueInitializationState;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};
use smallvec::{smallvec, SmallVec};
+use std::iter;
use std::mem;
use super::abs_domain::Lift;
@@ -21,7 +20,6 @@ struct MoveDataBuilder<'a, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
data: MoveData<'tcx>,
errors: Vec<(Place<'tcx>, MoveError<'tcx>)>,
- un_derefer: UnDerefer<'tcx>,
}
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
@@ -35,25 +33,29 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
tcx,
param_env,
errors: Vec::new(),
- un_derefer: UnDerefer { tcx: tcx, derefer_sidetable: Default::default() },
data: MoveData {
moves: IndexVec::new(),
loc_map: LocationMap::new(body),
rev_lookup: MovePathLookup {
locals: body
.local_decls
- .indices()
- .map(|i| {
- Self::new_move_path(
- &mut move_paths,
- &mut path_map,
- &mut init_path_map,
- None,
- Place::from(i),
+ .iter_enumerated()
+ .filter(|(_, l)| !l.is_deref_temp())
+ .map(|(i, _)| {
+ (
+ i,
+ Self::new_move_path(
+ &mut move_paths,
+ &mut path_map,
+ &mut init_path_map,
+ None,
+ Place::from(i),
+ ),
)
})
.collect(),
projections: Default::default(),
+ derefer_sidetable: Default::default(),
},
move_paths,
path_map,
@@ -98,13 +100,11 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
///
/// Maybe we should have separate "borrowck" and "moveck" modes.
fn move_path_for(&mut self, place: Place<'tcx>) -> Result<MovePathIndex, MoveError<'tcx>> {
- if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body)
- {
- return self.move_path_for(new_place);
- }
+ let deref_chain = self.builder.data.rev_lookup.deref_chain(place.as_ref());
debug!("lookup({:?})", place);
- let mut base = self.builder.data.rev_lookup.locals[place.local];
+ let mut base =
+ self.builder.data.rev_lookup.find_local(deref_chain.first().unwrap_or(&place).local);
// The move path index of the first union that we find. Once this is
// some we stop creating child move paths, since moves from unions
@@ -113,60 +113,55 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
// from `*(u.f: &_)` isn't allowed.
let mut union_path = None;
- for (i, elem) in place.projection.iter().enumerate() {
- let proj_base = &place.projection[..i];
- let body = self.builder.body;
- let tcx = self.builder.tcx;
- let place_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
- match place_ty.kind() {
- ty::Ref(..) | ty::RawPtr(..) => {
- let proj = &place.projection[..i + 1];
- return Err(MoveError::cannot_move_out_of(
- self.loc,
- BorrowedContent {
- target_place: Place {
- local: place.local,
- projection: tcx.mk_place_elems(proj),
+ for place in deref_chain.into_iter().chain(iter::once(place)) {
+ for (place_ref, elem) in place.as_ref().iter_projections() {
+ let body = self.builder.body;
+ let tcx = self.builder.tcx;
+ let place_ty = place_ref.ty(body, tcx).ty;
+ match place_ty.kind() {
+ ty::Ref(..) | ty::RawPtr(..) => {
+ return Err(MoveError::cannot_move_out_of(
+ self.loc,
+ BorrowedContent {
+ target_place: place_ref.project_deeper(&[elem], tcx),
},
- },
- ));
- }
- ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => {
- return Err(MoveError::cannot_move_out_of(
- self.loc,
- InteriorOfTypeWithDestructor { container_ty: place_ty },
- ));
- }
- ty::Adt(adt, _) if adt.is_union() => {
- union_path.get_or_insert(base);
- }
- ty::Slice(_) => {
- return Err(MoveError::cannot_move_out_of(
- self.loc,
- InteriorOfSliceOrArray {
- ty: place_ty,
- is_index: matches!(elem, ProjectionElem::Index(..)),
- },
- ));
- }
-
- ty::Array(..) => {
- if let ProjectionElem::Index(..) = elem {
+ ));
+ }
+ ty::Adt(adt, _) if adt.has_dtor(tcx) && !adt.is_box() => {
return Err(MoveError::cannot_move_out_of(
self.loc,
- InteriorOfSliceOrArray { ty: place_ty, is_index: true },
+ InteriorOfTypeWithDestructor { container_ty: place_ty },
+ ));
+ }
+ ty::Adt(adt, _) if adt.is_union() => {
+ union_path.get_or_insert(base);
+ }
+ ty::Slice(_) => {
+ return Err(MoveError::cannot_move_out_of(
+ self.loc,
+ InteriorOfSliceOrArray {
+ ty: place_ty,
+ is_index: matches!(elem, ProjectionElem::Index(..)),
+ },
));
}
- }
- _ => {}
- };
+ ty::Array(..) => {
+ if let ProjectionElem::Index(..) = elem {
+ return Err(MoveError::cannot_move_out_of(
+ self.loc,
+ InteriorOfSliceOrArray { ty: place_ty, is_index: true },
+ ));
+ }
+ }
- if union_path.is_none() {
- base = self.add_move_path(base, elem, |tcx| Place {
- local: place.local,
- projection: tcx.mk_place_elems(&place.projection[..i + 1]),
- });
+ _ => {}
+ };
+
+ if union_path.is_none() {
+ base = self
+ .add_move_path(base, elem, |tcx| place_ref.project_deeper(&[elem], tcx));
+ }
}
}
@@ -207,10 +202,8 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
}
}
-pub type MoveDat<'tcx> = Result<
- (FxHashMap<Local, Place<'tcx>>, MoveData<'tcx>),
- (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>),
->;
+pub type MoveDat<'tcx> =
+ Result<MoveData<'tcx>, (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>)>;
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn finalize(self) -> MoveDat<'tcx> {
@@ -226,11 +219,7 @@ impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
"done dumping moves"
});
- if self.errors.is_empty() {
- Ok((self.un_derefer.derefer_sidetable, self.data))
- } else {
- Err((self.data, self.errors))
- }
+ if self.errors.is_empty() { Ok(self.data) } else { Err((self.data, self.errors)) }
}
}
@@ -259,7 +248,7 @@ pub(super) fn gather_moves<'tcx>(
impl<'a, 'tcx> MoveDataBuilder<'a, 'tcx> {
fn gather_args(&mut self) {
for arg in self.body.args_iter() {
- let path = self.data.rev_lookup.locals[arg];
+ let path = self.data.rev_lookup.find_local(arg);
let init = self.data.inits.push(Init {
path,
@@ -295,7 +284,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
StatementKind::Assign(box (place, Rvalue::CopyForDeref(reffed))) => {
assert!(place.projection.is_empty());
if self.builder.body.local_decls[place.local].is_deref_temp() {
- self.builder.un_derefer.derefer_sidetable.insert(place.local, *reffed);
+ self.builder.data.rev_lookup.derefer_sidetable.insert(place.local, *reffed);
}
}
StatementKind::Assign(box (place, rval)) => {
@@ -317,7 +306,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
StatementKind::StorageLive(_) => {}
StatementKind::StorageDead(local) => {
// DerefTemp locals (results of CopyForDeref) don't actually move anything.
- if !self.builder.un_derefer.derefer_sidetable.contains_key(&local) {
+ if !self.builder.data.rev_lookup.derefer_sidetable.contains_key(&local) {
self.gather_move(Place::from(*local));
}
}
@@ -399,7 +388,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
destination,
target,
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
self.gather_operand(func);
@@ -459,12 +448,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_move(&mut self, place: Place<'tcx>) {
debug!("gather_move({:?}, {:?})", self.loc, place);
- if let Some(new_place) = self.builder.un_derefer.derefer(place.as_ref(), self.builder.body)
- {
- self.gather_move(new_place);
- return;
- }
-
if let [ref base @ .., ProjectionElem::Subslice { from, to, from_end: false }] =
**place.projection
{
@@ -521,11 +504,6 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
fn gather_init(&mut self, place: PlaceRef<'tcx>, kind: InitKind) {
debug!("gather_init({:?}, {:?})", self.loc, place);
- if let Some(new_place) = self.builder.un_derefer.derefer(place, self.builder.body) {
- self.gather_init(new_place.as_ref(), kind);
- return;
- }
-
let mut place = place;
// Check if we are assigning into a field of a union, if so, lookup the place
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
index ab1a67153..aa901f66d 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
@@ -1,5 +1,5 @@
use crate::move_paths::builder::MoveDat;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::mir::*;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
@@ -175,7 +175,7 @@ pub struct MoveData<'tcx> {
/// particular path being moved.)
pub loc_map: LocationMap<SmallVec<[MoveOutIndex; 4]>>,
pub path_map: IndexVec<MovePathIndex, SmallVec<[MoveOutIndex; 4]>>,
- pub rev_lookup: MovePathLookup,
+ pub rev_lookup: MovePathLookup<'tcx>,
pub inits: IndexVec<InitIndex, Init>,
/// Each Location `l` is mapped to the Inits that are effects
/// of executing the code at `l`.
@@ -289,8 +289,8 @@ impl Init {
/// Tables mapping from a place to its MovePathIndex.
#[derive(Debug)]
-pub struct MovePathLookup {
- locals: IndexVec<Local, MovePathIndex>,
+pub struct MovePathLookup<'tcx> {
+ locals: FxIndexMap<Local, MovePathIndex>,
/// projections are made from a base-place and a projection
/// elem. The base-place will have a unique MovePathIndex; we use
@@ -299,6 +299,9 @@ pub struct MovePathLookup {
/// base-place). For the remaining lookup, we map the projection
/// elem to the associated MovePathIndex.
projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>,
+
+ /// Maps `DerefTemp` locals to the `Place`s assigned to them.
+ derefer_sidetable: FxHashMap<Local, Place<'tcx>>,
}
mod builder;
@@ -309,27 +312,59 @@ pub enum LookupResult {
Parent(Option<MovePathIndex>),
}
-impl MovePathLookup {
+impl<'tcx> MovePathLookup<'tcx> {
// Unlike the builder `fn move_path_for` below, this lookup
// alternative will *not* create a MovePath on the fly for an
// unknown place, but will rather return the nearest available
// parent.
pub fn find(&self, place: PlaceRef<'_>) -> LookupResult {
- let mut result = self.locals[place.local];
+ let deref_chain = self.deref_chain(place);
- for elem in place.projection.iter() {
- if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
- result = subpath;
- } else {
+ let local = match deref_chain.first() {
+ Some(place) => place.local,
+ None => place.local,
+ };
+
+ let mut result = *self.locals.get(&local).unwrap_or_else(|| {
+ bug!("base local ({local:?}) of deref_chain should not be a deref temp")
+ });
+
+ // this needs to be a closure because `place` has a different lifetime than `prefix`'s places
+ let mut subpaths_for_place = |place: PlaceRef<'_>| {
+ for elem in place.projection.iter() {
+ if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
+ result = subpath;
+ } else {
+ return Some(result);
+ }
+ }
+ None
+ };
+
+ for place in deref_chain {
+ if let Some(result) = subpaths_for_place(place.as_ref()) {
return LookupResult::Parent(Some(result));
}
}
+ if let Some(result) = subpaths_for_place(place) {
+ return LookupResult::Parent(Some(result));
+ }
+
LookupResult::Exact(result)
}
pub fn find_local(&self, local: Local) -> MovePathIndex {
- self.locals[local]
+ let deref_chain = self.deref_chain(Place::from(local).as_ref());
+
+ let local = match deref_chain.last() {
+ Some(place) => place.local,
+ None => local,
+ };
+
+ *self.locals.get(&local).unwrap_or_else(|| {
+ bug!("base local ({local:?}) of deref_chain should not be a deref temp")
+ })
}
/// An enumerated iterator of `local`s and their associated
@@ -337,7 +372,22 @@ impl MovePathLookup {
pub fn iter_locals_enumerated(
&self,
) -> impl DoubleEndedIterator<Item = (Local, MovePathIndex)> + ExactSizeIterator + '_ {
- self.locals.iter_enumerated().map(|(l, &idx)| (l, idx))
+ self.locals.iter().map(|(&l, &idx)| (l, idx))
+ }
+
+ /// Returns the chain of places behind `DerefTemp` locals in `place`
+ pub fn deref_chain(&self, place: PlaceRef<'_>) -> Vec<Place<'tcx>> {
+ let mut prefix = Vec::new();
+ let mut local = place.local;
+
+ while let Some(&reffed) = self.derefer_sidetable.get(&local) {
+ prefix.insert(0, reffed);
+ local = reffed.local;
+ }
+
+ debug!("deref_chain({place:?}) = {prefix:?}");
+
+ prefix
}
}
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 7cae68efb..156231c3a 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -17,7 +17,7 @@ use crate::impls::{
use crate::move_paths::{HasMoveData, MoveData};
use crate::move_paths::{LookupResult, MovePathIndex};
use crate::MoveDataParamEnv;
-use crate::{Analysis, JoinSemiLattice, Results, ResultsCursor};
+use crate::{Analysis, JoinSemiLattice, ResultsCursor};
pub struct SanityCheck;
@@ -34,7 +34,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
}
let param_env = tcx.param_env(def_id);
- let (_, move_data) = MoveData::gather_moves(body, tcx, param_env).unwrap();
+ let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
let mdpe = MoveDataParamEnv { move_data, param_env };
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
@@ -42,7 +42,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &flow_inits);
+ sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
@@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &flow_uninits);
+ sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_definite_init).is_some() {
@@ -58,13 +58,13 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
.into_engine(tcx, body)
.iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &flow_def_inits);
+ sanity_check_via_rustc_peek(tcx, flow_def_inits.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint();
- sanity_check_via_rustc_peek(tcx, body, &flow_liveness);
+ sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body));
}
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
@@ -91,17 +91,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
/// errors are not intended to be used for unit tests.)
pub fn sanity_check_via_rustc_peek<'tcx, A>(
tcx: TyCtxt<'tcx>,
- body: &Body<'tcx>,
- results: &Results<'tcx, A>,
+ mut cursor: ResultsCursor<'_, 'tcx, A>,
) where
A: RustcPeekAt<'tcx>,
{
- let def_id = body.source.def_id();
+ let def_id = cursor.body().source.def_id();
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
- let mut cursor = ResultsCursor::new(body, results);
-
- let peek_calls = body.basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
+ let peek_calls = cursor.body().basic_blocks.iter_enumerated().filter_map(|(bb, block_data)| {
PeekCall::from_terminator(tcx, block_data.terminator()).map(|call| (bb, block_data, call))
});
@@ -132,8 +129,8 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>(
) => {
let loc = Location { block: bb, statement_index };
cursor.seek_before_primary_effect(loc);
- let state = cursor.get();
- results.analysis.peek_at(tcx, *place, state, call);
+ let (state, analysis) = cursor.get_with_analysis();
+ analysis.peek_at(tcx, *place, state, call);
}
_ => {
diff --git a/compiler/rustc_mir_dataflow/src/un_derefer.rs b/compiler/rustc_mir_dataflow/src/un_derefer.rs
deleted file mode 100644
index 7e6e25cc6..000000000
--- a/compiler/rustc_mir_dataflow/src/un_derefer.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::*;
-use rustc_middle::ty::TyCtxt;
-
-/// Used for reverting changes made by `DerefSeparator`
-pub struct UnDerefer<'tcx> {
- pub tcx: TyCtxt<'tcx>,
- pub derefer_sidetable: FxHashMap<Local, Place<'tcx>>,
-}
-
-impl<'tcx> UnDerefer<'tcx> {
- #[inline]
- pub fn derefer(&self, place: PlaceRef<'tcx>, body: &Body<'tcx>) -> Option<Place<'tcx>> {
- let reffed = self.derefer_sidetable.get(&place.local)?;
-
- let new_place = reffed.project_deeper(place.projection, self.tcx);
- if body.local_decls[new_place.local].is_deref_temp() {
- return self.derefer(new_place.as_ref(), body);
- }
- Some(new_place)
- }
-}
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index b74d06e5a..5693e5a4a 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -343,7 +343,7 @@ where
T: ValueAnalysis<'tcx>,
{
fn apply_statement_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
statement: &Statement<'tcx>,
_location: Location,
@@ -354,7 +354,7 @@ where
}
fn apply_terminator_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
terminator: &Terminator<'tcx>,
_location: Location,
@@ -365,7 +365,7 @@ where
}
fn apply_call_return_effect(
- &self,
+ &mut self,
state: &mut Self::Domain,
_block: BasicBlock,
return_places: crate::CallReturnPlaces<'_, 'tcx>,
@@ -376,7 +376,7 @@ where
}
fn apply_switch_int_edge_effects(
- &self,
+ &mut self,
_block: BasicBlock,
discr: &Operand<'tcx>,
apply_edge_effects: &mut impl SwitchIntEdgeEffects<Self::Domain>,
diff --git a/compiler/rustc_mir_transform/messages.ftl b/compiler/rustc_mir_transform/messages.ftl
index b13429d12..2598eb2ed 100644
--- a/compiler/rustc_mir_transform/messages.ftl
+++ b/compiler/rustc_mir_transform/messages.ftl
@@ -55,6 +55,8 @@ mir_transform_unaligned_packed_ref = reference to packed field is unaligned
mir_transform_union_access_label = access to union field
mir_transform_union_access_note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
mir_transform_unsafe_op_in_unsafe_fn = {$details} is unsafe and requires unsafe block (error E0133)
+ .suggestion = consider wrapping the function body in an unsafe block
+ .note = an unsafe function restricts its caller, but its body is safe by default
mir_transform_unused_unsafe = unnecessary `unsafe` block
.label = because it's nested under this `unsafe` block
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 187d38b38..d9e7339f1 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -28,6 +28,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
// References
ty::Ref(..) => true,
ty::Adt(..) if ty.is_box() => true,
+ ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true,
// Compound types: recurse
ty::Array(ty, _) | ty::Slice(ty) => {
// This does not branch so we keep the depth the same.
diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs
index ea0780688..4892ace53 100644
--- a/compiler/rustc_mir_transform/src/check_alignment.rs
+++ b/compiler/rustc_mir_transform/src/check_alignment.rs
@@ -155,7 +155,7 @@ fn insert_alignment_check<'tcx>(
new_block: BasicBlock,
) {
// Cast the pointer to a *const ()
- let const_raw_ptr = tcx.mk_ptr(TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
+ let const_raw_ptr = Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
let rvalue = Rvalue::Cast(CastKind::PtrToPtr, Operand::Copy(pointer), const_raw_ptr);
let thin_ptr = local_decls.push(LocalDecl::with_source_info(const_raw_ptr, source_info)).into();
block_data
@@ -240,7 +240,10 @@ fn insert_alignment_check<'tcx>(
required: Operand::Copy(alignment),
found: Operand::Copy(addr),
}),
- unwind: UnwindAction::Terminate,
+ // This calls panic_misaligned_pointer_dereference, which is #[rustc_nounwind].
+ // We never want to insert an unwind into unsafe code, because unwinding could
+ // make a failing UB check turn into much worse UB when we start unwinding.
+ unwind: UnwindAction::Unreachable,
},
});
}
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index 069514d8a..70812761e 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -421,10 +421,8 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
intravisit::walk_block(self, block);
}
- fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
- if matches!(self.tcx.def_kind(c.def_id), DefKind::InlineConst) {
- self.visit_body(self.tcx.hir().body(c.body))
- }
+ fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
+ self.visit_body(self.tcx.hir().body(c.body))
}
fn visit_fn(
@@ -527,6 +525,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
}
let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
+ // Only suggest wrapping the entire function body in an unsafe block once
+ let mut suggest_unsafe_block = true;
for &UnsafetyViolation { source_info, lint_root, kind, details } in violations.iter() {
let details = errors::RequiresUnsafeDetail { violation: details, span: source_info.span };
@@ -561,12 +561,29 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
op_in_unsafe_fn_allowed,
});
}
- UnsafetyViolationKind::UnsafeFn => tcx.emit_spanned_lint(
- UNSAFE_OP_IN_UNSAFE_FN,
- lint_root,
- source_info.span,
- errors::UnsafeOpInUnsafeFn { details },
- ),
+ UnsafetyViolationKind::UnsafeFn => {
+ tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ lint_root,
+ source_info.span,
+ errors::UnsafeOpInUnsafeFn {
+ details,
+ suggest_unsafe_block: suggest_unsafe_block.then(|| {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let fn_sig = tcx
+ .hir()
+ .fn_sig_by_hir_id(hir_id)
+ .expect("this violation only occurs in fn");
+ let body = tcx.hir().body_owned_by(def_id);
+ let body_span = tcx.hir().body(body).value.span;
+ let start = tcx.sess.source_map().start_point(body_span).shrink_to_hi();
+ let end = tcx.sess.source_map().end_point(body_span).shrink_to_lo();
+ (start, end, fn_sig.span)
+ }),
+ },
+ );
+ suggest_unsafe_block = false;
+ }
}
}
diff --git a/compiler/rustc_mir_transform/src/const_goto.rs b/compiler/rustc_mir_transform/src/const_goto.rs
index da101ca7a..e175f22d7 100644
--- a/compiler/rustc_mir_transform/src/const_goto.rs
+++ b/compiler/rustc_mir_transform/src/const_goto.rs
@@ -28,7 +28,9 @@ pub struct ConstGoto;
impl<'tcx> MirPass<'tcx> for ConstGoto {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
- sess.mir_opt_level() >= 4
+ // This pass participates in some as-of-yet untested unsoundness found
+ // in https://github.com/rust-lang/rust/issues/112460
+ sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs
index 1ba1951af..2f2c7357b 100644
--- a/compiler/rustc_mir_transform/src/const_prop.rs
+++ b/compiler/rustc_mir_transform/src/const_prop.rs
@@ -4,6 +4,7 @@
use either::Right;
use rustc_const_eval::const_eval::CheckAlignment;
+use rustc_const_eval::ReportErrorExt;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet;
@@ -37,6 +38,7 @@ macro_rules! throw_machine_stop_str {
($($tt:tt)*) => {{
// We make a new local type for it. The type itself does not carry any information,
// but its vtable (for the `MachineStopType` trait) does.
+ #[derive(Debug)]
struct Zst;
// Printing this type shows the desired string.
impl std::fmt::Display for Zst {
@@ -44,7 +46,17 @@ macro_rules! throw_machine_stop_str {
write!(f, $($tt)*)
}
}
- impl rustc_middle::mir::interpret::MachineStopType for Zst {}
+
+ impl rustc_middle::mir::interpret::MachineStopType for Zst {
+ fn diagnostic_message(&self) -> rustc_errors::DiagnosticMessage {
+ self.to_string().into()
+ }
+
+ fn add_args(
+ self: Box<Self>,
+ _: &mut dyn FnMut(std::borrow::Cow<'static, str>, rustc_errors::DiagnosticArgValue<'static>),
+ ) {}
+ }
throw_machine_stop!(Zst)
}};
}
@@ -103,7 +115,14 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
// That would require a 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);
- optimization_finder.visit_body(body);
+
+ // Traverse the body in reverse post-order, to ensure that `FullConstProp` locals are
+ // assigned before being read.
+ let rpo = body.basic_blocks.reverse_postorder().to_vec();
+ for bb in rpo {
+ let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
+ optimization_finder.visit_basic_block_data(bb, data);
+ }
trace!("ConstProp done for {:?}", def_id);
}
@@ -367,7 +386,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
op
}
Err(e) => {
- trace!("get_const failed: {}", e);
+ trace!("get_const failed: {:?}", e.into_kind().debug());
return None;
}
};
@@ -789,12 +808,6 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
self.tcx
}
- fn visit_body(&mut self, body: &mut Body<'tcx>) {
- for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
- self.visit_basic_block_data(bb, data);
- }
- }
-
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
self.super_operand(operand, location);
@@ -885,14 +898,23 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
}
StatementKind::StorageLive(local) => {
- let frame = self.ecx.frame_mut();
- frame.locals[local].value =
- LocalValue::Live(interpret::Operand::Immediate(interpret::Immediate::Uninit));
- }
- StatementKind::StorageDead(local) => {
- let frame = self.ecx.frame_mut();
- frame.locals[local].value = LocalValue::Dead;
+ Self::remove_const(&mut self.ecx, local);
}
+ // We do not need to mark dead locals as such. For `FullConstProp` locals,
+ // this allows to propagate the single assigned value in this case:
+ // ```
+ // let x = SOME_CONST;
+ // if a {
+ // f(copy x);
+ // StorageDead(x);
+ // } else {
+ // g(copy x);
+ // StorageDead(x);
+ // }
+ // ```
+ //
+ // This may propagate a constant where the local would be uninit or dead.
+ // In both cases, this does not matter, as those reads would be UB anyway.
_ => {}
}
}
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs
index 0fe49b8a1..759650fe4 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -9,6 +9,7 @@ use rustc_const_eval::interpret::Immediate;
use rustc_const_eval::interpret::{
self, InterpCx, InterpResult, LocalValue, MemoryKind, OpTy, Scalar, StackPopCleanup,
};
+use rustc_const_eval::ReportErrorExt;
use rustc_hir::def::DefKind;
use rustc_hir::HirId;
use rustc_index::bit_set::BitSet;
@@ -232,7 +233,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
op
}
Err(e) => {
- trace!("get_const failed: {}", e);
+ trace!("get_const failed: {:?}", e.into_kind().debug());
return None;
}
};
@@ -272,8 +273,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
// dedicated error variants should be introduced instead.
assert!(
!error.kind().formatted_string(),
- "const-prop encountered formatting error: {}",
- error
+ "const-prop encountered formatting error: {error:?}",
);
None
}
diff --git a/compiler/rustc_mir_transform/src/coverage/debug.rs b/compiler/rustc_mir_transform/src/coverage/debug.rs
index 6a3d42511..7ad981441 100644
--- a/compiler/rustc_mir_transform/src/coverage/debug.rs
+++ b/compiler/rustc_mir_transform/src/coverage/debug.rs
@@ -277,14 +277,7 @@ impl DebugCounters {
pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option<String>) {
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
- ),
- };
+ let id = counter_kind.as_operand_id();
counters
.try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
.expect("attempt to add the same counter_kind to DebugCounters more than once");
@@ -330,13 +323,7 @@ impl DebugCounters {
}
}
- 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)
- }
- };
+ let id = counter_kind.as_operand_id();
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), .. }) =
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index ea1223fbc..d2a854b26 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -387,7 +387,7 @@ impl BasicCoverageBlockData {
// 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()) {
+ if self.counter_kind.as_ref().is_some_and(|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`",
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index d27200419..35cf9ea5f 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -480,9 +480,10 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
fn check_invoked_macro_name_span(&mut self) {
if let Some(visible_macro) = self.curr().visible_macro(self.body_span) {
- if self.prev_expn_span.map_or(true, |prev_expn_span| {
- self.curr().expn_span.ctxt() != prev_expn_span.ctxt()
- }) {
+ if !self
+ .prev_expn_span
+ .is_some_and(|prev_expn_span| self.curr().expn_span.ctxt() == prev_expn_span.ctxt())
+ {
let merged_prefix_len = self.curr_original_span.lo() - self.curr().span.lo();
let after_macro_bang =
merged_prefix_len + BytePos(visible_macro.as_str().len() as u32 + 1);
diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs
index 90b58933d..25891d3ca 100644
--- a/compiler/rustc_mir_transform/src/coverage/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/tests.rs
@@ -140,7 +140,7 @@ impl<'tcx> MockBlocks<'tcx> {
destination: self.dummy_place.clone(),
target: Some(TEMP_BLOCK),
unwind: UnwindAction::Continue,
- from_hir_call: false,
+ call_source: CallSource::Misc,
fn_span: DUMMY_SP,
},
)
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 7adfc9dff..78fb19635 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -10,8 +10,12 @@ use rustc_middle::mir::visit::{MutVisitor, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, ValueOrPlace};
-use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
+use rustc_mir_dataflow::value_analysis::{
+ Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
+};
+use rustc_mir_dataflow::{
+ lattice::FlatSet, Analysis, Results, ResultsVisitor, SwitchIntEdgeEffects,
+};
use rustc_span::DUMMY_SP;
use rustc_target::abi::{Align, FieldIdx, VariantIdx};
@@ -52,11 +56,11 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
// Perform the actual dataflow analysis.
let analysis = ConstAnalysis::new(tcx, body, map);
- let results = debug_span!("analyze")
+ let mut results = debug_span!("analyze")
.in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint());
// Collect results and patch the body afterwards.
- let mut visitor = CollectAndPatch::new(tcx, &results.analysis.0.map);
+ let mut visitor = CollectAndPatch::new(tcx);
debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
debug_span!("patch").in_scope(|| visitor.visit_body(body));
}
@@ -387,9 +391,8 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
}
}
-struct CollectAndPatch<'tcx, 'map> {
+struct CollectAndPatch<'tcx> {
tcx: TyCtxt<'tcx>,
- map: &'map Map,
/// For a given MIR location, this stores the values of the operands used by that location. In
/// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are
@@ -400,9 +403,9 @@ struct CollectAndPatch<'tcx, 'map> {
assignments: FxHashMap<Location, ScalarTy<'tcx>>,
}
-impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> {
- fn new(tcx: TyCtxt<'tcx>, map: &'map Map) -> Self {
- Self { tcx, map, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
+impl<'tcx> CollectAndPatch<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> Self {
+ Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
}
fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> {
@@ -414,18 +417,23 @@ impl<'tcx, 'map> CollectAndPatch<'tcx, 'map> {
}
}
-impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map> {
+impl<'mir, 'tcx>
+ ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
+ for CollectAndPatch<'tcx>
+{
type FlowState = State<FlatSet<ScalarTy<'tcx>>>;
fn visit_statement_before_primary_effect(
&mut self,
+ results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
statement: &'mir Statement<'tcx>,
location: Location,
) {
match &statement.kind {
StatementKind::Assign(box (_, rvalue)) => {
- OperandCollector { state, visitor: self }.visit_rvalue(rvalue, location);
+ OperandCollector { state, visitor: self, map: &results.analysis.0.map }
+ .visit_rvalue(rvalue, location);
}
_ => (),
}
@@ -433,6 +441,7 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map
fn visit_statement_after_primary_effect(
&mut self,
+ results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
statement: &'mir Statement<'tcx>,
location: Location,
@@ -441,30 +450,34 @@ impl<'mir, 'tcx, 'map> ResultsVisitor<'mir, 'tcx> for CollectAndPatch<'tcx, 'map
StatementKind::Assign(box (_, Rvalue::Use(Operand::Constant(_)))) => {
// Don't overwrite the assignment if it already uses a constant (to keep the span).
}
- StatementKind::Assign(box (place, _)) => match state.get(place.as_ref(), self.map) {
- FlatSet::Top => (),
- FlatSet::Elem(value) => {
- self.assignments.insert(location, value);
- }
- FlatSet::Bottom => {
- // This assignment is either unreachable, or an uninitialized value is assigned.
+ StatementKind::Assign(box (place, _)) => {
+ match state.get(place.as_ref(), &results.analysis.0.map) {
+ FlatSet::Top => (),
+ FlatSet::Elem(value) => {
+ self.assignments.insert(location, value);
+ }
+ FlatSet::Bottom => {
+ // This assignment is either unreachable, or an uninitialized value is assigned.
+ }
}
- },
+ }
_ => (),
}
}
fn visit_terminator_before_primary_effect(
&mut self,
+ results: &Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
state: &Self::FlowState,
terminator: &'mir Terminator<'tcx>,
location: Location,
) {
- OperandCollector { state, visitor: self }.visit_terminator(terminator, location);
+ OperandCollector { state, visitor: self, map: &results.analysis.0.map }
+ .visit_terminator(terminator, location);
}
}
-impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> {
+impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
self.tcx
}
@@ -496,14 +509,15 @@ impl<'tcx, 'map> MutVisitor<'tcx> for CollectAndPatch<'tcx, 'map> {
struct OperandCollector<'tcx, 'map, 'a> {
state: &'a State<FlatSet<ScalarTy<'tcx>>>,
- visitor: &'a mut CollectAndPatch<'tcx, 'map>,
+ visitor: &'a mut CollectAndPatch<'tcx>,
+ map: &'map Map,
}
impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
match operand {
Operand::Copy(place) | Operand::Move(place) => {
- match self.state.get(place.as_ref(), self.visitor.map) {
+ match self.state.get(place.as_ref(), self.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.visitor.before_effect.insert((location, *place), value);
diff --git a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
index a133c9d47..e782c0373 100644
--- a/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
+++ b/compiler/rustc_mir_transform/src/deduce_param_attrs.rs
@@ -203,7 +203,12 @@ pub fn deduced_param_attrs<'tcx>(
body.local_decls.iter().skip(1).take(body.arg_count).enumerate().map(
|(arg_index, local_decl)| DeducedParamAttrs {
read_only: !deduce_read_only.mutable_args.contains(arg_index)
- && local_decl.ty.is_freeze(tcx, param_env),
+ // We must normalize here to reveal opaques and normalize
+ // their substs, otherwise we'll see exponential blow-up in
+ // compile times: #113372
+ && tcx
+ .normalize_erasing_regions(param_env, local_decl.ty)
+ .is_freeze(tcx, param_env),
},
),
);
diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs
index a39026751..95898b5b7 100644
--- a/compiler/rustc_mir_transform/src/deref_separator.rs
+++ b/compiler/rustc_mir_transform/src/deref_separator.rs
@@ -8,13 +8,13 @@ use rustc_middle::ty::TyCtxt;
pub struct Derefer;
-pub struct DerefChecker<'tcx> {
+pub struct DerefChecker<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
patcher: MirPatch<'tcx>,
- local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ local_decls: &'a IndexVec<Local, LocalDecl<'tcx>>,
}
-impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
+impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
@@ -36,7 +36,7 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
if !p_ref.projection.is_empty() && p_elem == ProjectionElem::Deref {
- let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
+ let ty = p_ref.ty(self.local_decls, self.tcx).ty;
let temp = self.patcher.new_internal_with_info(
ty,
self.local_decls[p_ref.local].source_info.span,
@@ -70,7 +70,7 @@ impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let patch = MirPatch::new(body);
- let mut checker = DerefChecker { tcx, patcher: patch, local_decls: body.local_decls.clone() };
+ let mut checker = DerefChecker { tcx, patcher: patch, local_decls: &body.local_decls };
for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
checker.visit_basic_block_data(bb, data);
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 78758e2db..a31551cf6 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -37,6 +37,10 @@
//! if they do not consistently refer to the same place in memory. This is satisfied if they do
//! not contain any indirection through a pointer or any indexing projections.
//!
+//! * `p` and `q` must have the **same type**. If we replace a local with a subtype or supertype,
+//! we may end up with a differnet vtable for that local. See the `subtyping-impacts-selection`
+//! tests for an example where that causes issues.
+//!
//! * We need to make sure that the goal of "merging the memory" is actually structurally possible
//! in MIR. For example, even if all the other conditions are satisfied, there is no way to
//! "merge" `_5.foo` and `_6.bar`. For now, we ensure this by requiring that both `p` and `q` are
@@ -134,6 +138,7 @@ use crate::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::HasLocalDecls;
use rustc_middle::mir::{dump_mir, PassWhere};
use rustc_middle::mir::{
traversal, Body, InlineAsmOperand, Local, LocalKind, Location, Operand, Place, Rvalue,
@@ -763,12 +768,22 @@ impl<'tcx> Visitor<'tcx> for FindAssignments<'_, '_, 'tcx> {
return;
};
- // As described at the top of the file, we do not go near things that have their address
- // taken.
+ // As described at the top of the file, we do not go near things that have
+ // their address taken.
if self.borrowed.contains(src) || self.borrowed.contains(dest) {
return;
}
+ // As described at the top of this file, we do not touch locals which have
+ // different types.
+ let src_ty = self.body.local_decls()[src].ty;
+ let dest_ty = self.body.local_decls()[dest].ty;
+ if src_ty != dest_ty {
+ // FIXME(#112651): This can be removed afterwards. Also update the module description.
+ trace!("skipped `{src:?} = {dest:?}` due to subtyping: {src_ty} != {dest_ty}");
+ return;
+ }
+
// Also, we need to make sure that MIR actually allows the `src` to be removed
if is_local_required(src, self.body) {
return;
diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs
index 746e3d965..13841be49 100644
--- a/compiler/rustc_mir_transform/src/dump_mir.rs
+++ b/compiler/rustc_mir_transform/src/dump_mir.rs
@@ -7,7 +7,7 @@ use crate::MirPass;
use rustc_middle::mir::write_mir_pretty;
use rustc_middle::mir::Body;
use rustc_middle::ty::TyCtxt;
-use rustc_session::config::OutputType;
+use rustc_session::config::{OutFileName, OutputType};
pub struct Marker(pub &'static str);
@@ -20,8 +20,15 @@ impl<'tcx> MirPass<'tcx> for Marker {
}
pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> {
- let path = tcx.output_filenames(()).path(OutputType::Mir);
- let mut f = io::BufWriter::new(File::create(&path)?);
- write_mir_pretty(tcx, None, &mut f)?;
+ match tcx.output_filenames(()).path(OutputType::Mir) {
+ OutFileName::Stdout => {
+ let mut f = io::stdout();
+ write_mir_pretty(tcx, None, &mut f)?;
+ }
+ OutFileName::Real(path) => {
+ let mut f = io::BufWriter::new(File::create(&path)?);
+ write_mir_pretty(tcx, None, &mut f)?;
+ }
+ }
Ok(())
}
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index f31653caa..cc0d7d51b 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -21,7 +21,7 @@ pub fn build_ptr_tys<'tcx>(
let substs = tcx.mk_substs(&[pointee.into()]);
let unique_ty = tcx.type_of(unique_did).subst(tcx, substs);
let nonnull_ty = tcx.type_of(nonnull_did).subst(tcx, substs);
- let ptr_ty = tcx.mk_imm_ptr(pointee);
+ let ptr_ty = Ty::new_imm_ptr(tcx, pointee);
(unique_ty, nonnull_ty, ptr_ty)
}
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index fda0e1023..d5664e2b4 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -10,7 +10,6 @@ use rustc_mir_dataflow::elaborate_drops::{DropElaborator, DropFlagMode, DropStyl
use rustc_mir_dataflow::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
use rustc_mir_dataflow::on_lookup_result_bits;
-use rustc_mir_dataflow::un_derefer::UnDerefer;
use rustc_mir_dataflow::MoveDataParamEnv;
use rustc_mir_dataflow::{on_all_children_bits, on_all_drop_children_bits};
use rustc_mir_dataflow::{Analysis, ResultsCursor};
@@ -54,20 +53,19 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
let def_id = body.source.def_id();
let param_env = tcx.param_env_reveal_all_normalized(def_id);
- let (side_table, move_data) = match MoveData::gather_moves(body, tcx, param_env) {
+ let move_data = match MoveData::gather_moves(body, tcx, param_env) {
Ok(move_data) => move_data,
Err((move_data, _)) => {
tcx.sess.delay_span_bug(
body.span,
"No `move_errors` should be allowed in MIR borrowck",
);
- (Default::default(), move_data)
+ move_data
}
};
- let un_derefer = UnDerefer { tcx: tcx, derefer_sidetable: side_table };
let elaborate_patch = {
let env = MoveDataParamEnv { move_data, param_env };
- remove_dead_unwinds(tcx, body, &env, &un_derefer);
+ remove_dead_unwinds(tcx, body, &env);
let inits = MaybeInitializedPlaces::new(tcx, body, &env)
.into_engine(tcx, body)
@@ -92,7 +90,6 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
init_data: InitializationData { inits, uninits },
drop_flags,
patch: MirPatch::new(body),
- un_derefer: un_derefer,
reachable,
}
.elaborate()
@@ -108,7 +105,6 @@ fn remove_dead_unwinds<'tcx>(
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
env: &MoveDataParamEnv<'tcx>,
- und: &UnDerefer<'tcx>,
) {
debug!("remove_dead_unwinds({:?})", body.span);
// We only need to do this pass once, because unwind edges can only
@@ -121,9 +117,7 @@ fn remove_dead_unwinds<'tcx>(
.into_results_cursor(body);
for (bb, bb_data) in body.basic_blocks.iter_enumerated() {
let place = match bb_data.terminator().kind {
- TerminatorKind::Drop { ref place, unwind: UnwindAction::Cleanup(_), .. } => {
- und.derefer(place.as_ref(), body).unwrap_or(*place)
- }
+ TerminatorKind::Drop { place, unwind: UnwindAction::Cleanup(_), .. } => place,
_ => continue,
};
@@ -296,7 +290,6 @@ struct ElaborateDropsCtxt<'a, 'tcx> {
init_data: InitializationData<'a, 'tcx>,
drop_flags: IndexVec<MovePathIndex, Option<Local>>,
patch: MirPatch<'tcx>,
- un_derefer: UnDerefer<'tcx>,
reachable: BitSet<BasicBlock>,
}
@@ -342,9 +335,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
}
let terminator = data.terminator();
let place = match terminator.kind {
- TerminatorKind::Drop { ref place, .. } => {
- self.un_derefer.derefer(place.as_ref(), self.body).unwrap_or(*place)
- }
+ TerminatorKind::Drop { ref place, .. } => place,
_ => continue,
};
@@ -401,11 +392,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
let terminator = data.terminator();
match terminator.kind {
- TerminatorKind::Drop { mut place, target, unwind, replace } => {
- if let Some(new_place) = self.un_derefer.derefer(place.as_ref(), self.body) {
- place = new_place;
- }
-
+ TerminatorKind::Drop { place, target, unwind, replace } => {
self.init_data.seek_before(loc);
match self.move_data().rev_lookup.find(place.as_ref()) {
LookupResult::Exact(path) => {
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 602e40d51..4b796d79e 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -1,5 +1,6 @@
use rustc_errors::{
- DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler, IntoDiagnostic,
+ Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Handler,
+ IntoDiagnostic,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::mir::{AssertKind, UnsafetyViolationDetails};
@@ -130,6 +131,12 @@ impl RequiresUnsafeDetail {
pub(crate) struct UnsafeOpInUnsafeFn {
pub details: RequiresUnsafeDetail,
+
+ /// These spans point to:
+ /// 1. the start of the function body
+ /// 2. the end of the function body
+ /// 3. the function signature
+ pub suggest_unsafe_block: Option<(Span, Span, Span)>,
}
impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
@@ -138,13 +145,21 @@ impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
self,
diag: &'b mut DiagnosticBuilder<'a, ()>,
) -> &'b mut DiagnosticBuilder<'a, ()> {
- let desc = diag
- .handler()
- .expect("lint should not yet be emitted")
- .eagerly_translate_to_string(self.details.label(), [].into_iter());
+ let handler = diag.handler().expect("lint should not yet be emitted");
+ let desc = handler.eagerly_translate_to_string(self.details.label(), [].into_iter());
diag.set_arg("details", desc);
diag.span_label(self.details.span, self.details.label());
diag.note(self.details.note());
+
+ if let Some((start, end, fn_sig)) = self.suggest_unsafe_block {
+ diag.span_note(fn_sig, crate::fluent_generated::mir_transform_note);
+ diag.tool_only_multipart_suggestion(
+ crate::fluent_generated::mir_transform_suggestion,
+ vec![(start, " unsafe {".into()), (end, "}".into())],
+ Applicability::MaybeIncorrect,
+ );
+ }
+
diag
}
@@ -163,7 +178,14 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
self,
diag: &'b mut DiagnosticBuilder<'a, ()>,
) -> &'b mut DiagnosticBuilder<'a, ()> {
- diag.span_label(self.span(), format!("{:?}", self.panic()));
+ let span = self.span();
+ let assert_kind = self.panic();
+ let message = assert_kind.diagnostic_message();
+ assert_kind.add_args(&mut |name, value| {
+ diag.set_arg(name, value);
+ });
+ diag.span_label(span, message);
+
diag
}
@@ -191,7 +213,7 @@ impl<P> AssertLint<P> {
AssertLint::ArithmeticOverflow(sp, _) | AssertLint::UnconditionalPanic(sp, _) => *sp,
}
}
- pub fn panic(&self) -> &AssertKind<P> {
+ pub fn panic(self) -> AssertKind<P> {
match self {
AssertLint::ArithmeticOverflow(_, p) | AssertLint::UnconditionalPanic(_, p) => p,
}
diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs
index 5989dbebf..0b41e57be 100644
--- a/compiler/rustc_mir_transform/src/function_item_references.rs
+++ b/compiler/rustc_mir_transform/src/function_item_references.rs
@@ -2,7 +2,7 @@ use itertools::Itertools;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, EarlyBinder, PredicateKind, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt};
use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
use rustc_span::{symbol::sym, Span};
use rustc_target::spec::abi::Abi;
@@ -34,7 +34,7 @@ impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
destination: _,
target: _,
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} = &terminator.kind
{
@@ -74,7 +74,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
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.kind().skip_binder()) {
+ if let Some(bound_ty) = self.is_pointer_trait(bound) {
// Get the argument types as they appear in the function signature.
let arg_defs = self.tcx.fn_sig(def_id).subst_identity().skip_binder().inputs();
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
@@ -83,7 +83,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
// If the inner type matches the type bound by `Pointer`
if inner_ty == bound_ty {
// Do a substitution using the parameters from the callsite
- let subst_ty = EarlyBinder(inner_ty).subst(self.tcx, substs_ref);
+ let subst_ty = EarlyBinder::bind(inner_ty).subst(self.tcx, substs_ref);
if let Some((fn_id, fn_substs)) =
FunctionItemRefChecker::is_fn_ref(subst_ty)
{
@@ -104,8 +104,8 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
}
/// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type.
- fn is_pointer_trait(&self, bound: &PredicateKind<'tcx>) -> Option<Ty<'tcx>> {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) = bound {
+ fn is_pointer_trait(&self, bound: ty::Clause<'tcx>) -> Option<Ty<'tcx>> {
+ if let ty::ClauseKind::Trait(predicate) = bound.kind().skip_binder() {
self.tcx
.is_diagnostic_item(sym::Pointer, predicate.def_id())
.then(|| predicate.trait_ref.self_ty())
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 89567ed0a..264bc61f1 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -230,7 +230,7 @@ struct TransformVisitor<'tcx> {
// Mapping from Local to (type of local, generator struct index)
// FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
- remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
+ remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>,
// A map from a suspension point in a block to the locals which have live storage at that point
storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
@@ -295,11 +295,11 @@ impl<'tcx> TransformVisitor<'tcx> {
}
// Create a Place referencing a generator struct field
- fn make_field(&self, variant_index: VariantIdx, idx: usize, ty: Ty<'tcx>) -> Place<'tcx> {
+ fn make_field(&self, variant_index: VariantIdx, idx: FieldIdx, ty: Ty<'tcx>) -> Place<'tcx> {
let self_place = Place::from(SELF_ARG);
let base = self.tcx.mk_place_downcast_unnamed(self_place, variant_index);
let mut projection = base.projection.to_vec();
- projection.push(ProjectionElem::Field(FieldIdx::new(idx), ty));
+ projection.push(ProjectionElem::Field(idx, ty));
Place { local: base.local, projection: self.tcx.mk_place_elems(&projection) }
}
@@ -413,8 +413,11 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
fn make_generator_state_argument_indirect<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let gen_ty = body.local_decls.raw[1].ty;
- let ref_gen_ty =
- tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut });
+ let ref_gen_ty = Ty::new_ref(
+ tcx,
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty: gen_ty, mutbl: Mutability::Mut },
+ );
// Replace the by value generator argument
body.local_decls.raw[1].ty = ref_gen_ty;
@@ -429,7 +432,7 @@ fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
let pin_did = tcx.require_lang_item(LangItem::Pin, Some(body.span));
let pin_adt_ref = tcx.adt_def(pin_did);
let substs = tcx.mk_substs(&[ref_gen_ty.into()]);
- let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
+ let pin_ref_gen_ty = Ty::new_adt(tcx, pin_adt_ref, substs);
// Replace the by ref generator argument
body.local_decls.raw[1].ty = pin_ref_gen_ty;
@@ -481,7 +484,7 @@ fn replace_local<'tcx>(
/// still using the `ResumeTy` indirection for the time being, and that indirection
/// is removed here. After this transform, the generator body only knows about `&mut Context<'_>`.
fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let context_mut_ref = tcx.mk_task_context();
+ let context_mut_ref = Ty::new_task_context(tcx);
// replace the type of the `resume` argument
replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref);
@@ -597,16 +600,15 @@ fn locals_live_across_suspend_points<'tcx>(
let borrowed_locals_results =
MaybeBorrowedLocals.into_engine(tcx, body_ref).pass_name("generator").iterate_to_fixpoint();
- let mut borrowed_locals_cursor =
- rustc_mir_dataflow::ResultsCursor::new(body_ref, &borrowed_locals_results);
+ let mut borrowed_locals_cursor = borrowed_locals_results.cloned_results_cursor(body_ref);
// 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)
- .iterate_to_fixpoint();
- let mut requires_storage_cursor =
- rustc_mir_dataflow::ResultsCursor::new(body_ref, &requires_storage_results);
+ let mut requires_storage_results =
+ MaybeRequiresStorage::new(borrowed_locals_results.cloned_results_cursor(body))
+ .into_engine(tcx, body_ref)
+ .iterate_to_fixpoint();
+ let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body_ref);
// Calculate the liveness of MIR locals ignoring borrows.
let mut liveness = MaybeLiveLocals
@@ -747,7 +749,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &GeneratorSavedLocals,
always_live_locals: BitSet<Local>,
- requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
+ mut requires_storage: rustc_mir_dataflow::Results<'tcx, MaybeRequiresStorage<'_, 'mir, 'tcx>>,
) -> BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal> {
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
@@ -802,13 +804,14 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> {
local_conflicts: BitMatrix<Local, Local>,
}
-impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
+impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
for StorageConflictVisitor<'mir, 'tcx, '_>
{
type FlowState = BitSet<Local>;
fn visit_statement_before_primary_effect(
&mut self,
+ _results: &R,
state: &Self::FlowState,
_statement: &'mir Statement<'tcx>,
loc: Location,
@@ -818,6 +821,7 @@ impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
fn visit_terminator_before_primary_effect(
&mut self,
+ _results: &R,
state: &Self::FlowState,
_terminator: &'mir Terminator<'tcx>,
loc: Location,
@@ -903,7 +907,7 @@ fn compute_layout<'tcx>(
liveness: LivenessInfo,
body: &Body<'tcx>,
) -> (
- FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
+ FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>,
GeneratorLayout<'tcx>,
IndexVec<BasicBlock, Option<BitSet<Local>>>,
) {
@@ -981,6 +985,7 @@ fn compute_layout<'tcx>(
// just use the first one here. That's fine; fields do not move
// around inside generators, so it doesn't matter which variant
// index we access them by.
+ let idx = FieldIdx::from_usize(idx);
remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx));
}
variant_fields.push(fields);
@@ -989,8 +994,23 @@ fn compute_layout<'tcx>(
debug!("generator variant_fields = {:?}", variant_fields);
debug!("generator storage_conflicts = {:#?}", storage_conflicts);
- let layout =
- GeneratorLayout { field_tys: tys, variant_fields, variant_source_info, storage_conflicts };
+ let mut field_names = IndexVec::from_elem(None, &tys);
+ for var in &body.var_debug_info {
+ let VarDebugInfoContents::Place(place) = &var.value else { continue };
+ let Some(local) = place.as_local() else { continue };
+ let Some(&(_, variant, field)) = remap.get(&local) else { continue };
+
+ let saved_local = variant_fields[variant][field];
+ field_names.get_or_insert_with(saved_local, || var.name);
+ }
+
+ let layout = GeneratorLayout {
+ field_tys: tys,
+ field_names,
+ variant_fields,
+ variant_source_info,
+ storage_conflicts,
+ };
debug!(?layout);
(remap, layout, storage_liveness)
@@ -1113,13 +1133,13 @@ fn create_generator_drop_shim<'tcx>(
}
// Replace the return variable
- body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.mk_unit(), source_info);
+ body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(Ty::new_unit(tcx), source_info);
make_generator_state_argument_indirect(tcx, &mut body);
// Change the generator argument from &mut to *mut
body.local_decls[SELF_ARG] = LocalDecl::with_source_info(
- tcx.mk_ptr(ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
+ Ty::new_ptr(tcx, ty::TypeAndMut { ty: gen_ty, mutbl: hir::Mutability::Mut }),
source_info,
);
@@ -1476,7 +1496,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
let state_substs = tcx.mk_substs(&[yield_ty.into(), body.return_ty().into()]);
(state_adt_ref, state_substs)
};
- let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+ let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_substs);
// We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
// RETURN_PLACE then is a fresh unused local with type ret_ty.
@@ -1492,8 +1512,11 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
// case there is no `Assign` to it that the transform can turn into a store to the generator
// state. After the yield the slot in the generator state would then be uninitialized.
let resume_local = Local::new(2);
- let resume_ty =
- if is_async_kind { tcx.mk_task_context() } else { body.local_decls[resume_local].ty };
+ let resume_ty = if is_async_kind {
+ Ty::new_task_context(tcx)
+ } else {
+ body.local_decls[resume_local].ty
+ };
let new_resume_local = replace_local(resume_local, resume_ty, body, tcx);
// When first entering the generator, move the resume argument into its new local.
@@ -1691,7 +1714,7 @@ impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
destination,
target: Some(_),
unwind: _,
- from_hir_call: _,
+ call_source: _,
fn_span: _,
} => {
self.check_assigned_place(*destination, |this| {
@@ -1807,7 +1830,7 @@ fn check_must_not_suspend_ty<'tcx>(
let mut has_emitted = false;
for &(predicate, _) in tcx.explicit_item_bounds(def).skip_binder() {
// We only look at the `DefId`, so it is safe to skip the binder here.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
+ if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
predicate.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 233de6dc6..b6578cb25 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -145,13 +145,16 @@ impl<'tcx> Inliner<'tcx> {
Ok(new_blocks) => {
debug!("inlined {}", callsite.callee);
self.changed = true;
+
+ self.history.push(callsite.callee.def_id());
+ self.process_blocks(caller_body, new_blocks);
+ self.history.pop();
+
inlined_count += 1;
if inlined_count == inline_limit {
+ debug!("inline count reached");
return;
}
- self.history.push(callsite.callee.def_id());
- self.process_blocks(caller_body, new_blocks);
- self.history.pop();
}
}
}
@@ -192,7 +195,7 @@ impl<'tcx> Inliner<'tcx> {
let Ok(callee_body) = callsite.callee.try_subst_mir_and_normalize_erasing_regions(
self.tcx,
self.param_env,
- ty::EarlyBinder(callee_body.clone()),
+ ty::EarlyBinder::bind(callee_body.clone()),
) else {
return Err("failed to normalize callee body");
};
@@ -459,7 +462,7 @@ impl<'tcx> Inliner<'tcx> {
// If the place doesn't actually need dropping, treat it like a regular goto.
let ty = callsite
.callee
- .subst_mir(self.tcx, ty::EarlyBinder(&place.ty(callee_body, tcx).ty));
+ .subst_mir(self.tcx, ty::EarlyBinder::bind(&place.ty(callee_body, tcx).ty));
if ty.needs_drop(tcx, self.param_env) && let UnwindAction::Cleanup(unwind) = unwind {
work_list.push(unwind);
}
@@ -480,11 +483,12 @@ impl<'tcx> Inliner<'tcx> {
// Abort if type validation found anything fishy.
checker.validation?;
+ // N.B. We still apply our cost threshold to #[inline(always)] functions.
+ // That attribute is often applied to very large functions that exceed LLVM's (very
+ // generous) inlining threshold. Such functions are very poor MIR inlining candidates.
+ // Always inlining #[inline(always)] functions in MIR, on net, slows down the compiler.
let cost = checker.cost;
- if let InlineAttr::Always = callee_attrs.inline {
- debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost);
- Ok(())
- } else if cost <= threshold {
+ if cost <= threshold {
debug!("INLINING {:?} [cost={} <= threshold={}]", callsite, cost, threshold);
Ok(())
} else {
@@ -522,7 +526,7 @@ impl<'tcx> Inliner<'tcx> {
trace!("creating temp for return destination");
let dest = Rvalue::Ref(
self.tcx.lifetimes.re_erased,
- BorrowKind::Mut { allow_two_phase_borrow: false },
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
destination,
);
let dest_ty = dest.ty(caller_body, self.tcx);
@@ -794,7 +798,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
// If the place doesn't actually need dropping, treat it like a regular goto.
let ty = self
.instance
- .subst_mir(tcx, ty::EarlyBinder(&place.ty(self.callee_body, tcx).ty));
+ .subst_mir(tcx, ty::EarlyBinder::bind(&place.ty(self.callee_body, tcx).ty));
if ty.needs_drop(tcx, self.param_env) {
self.cost += CALL_PENALTY;
if let UnwindAction::Cleanup(_) = unwind {
@@ -805,7 +809,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
}
}
TerminatorKind::Call { func: Operand::Constant(ref f), unwind, .. } => {
- let fn_ty = self.instance.subst_mir(tcx, ty::EarlyBinder(&f.literal.ty()));
+ let fn_ty = self.instance.subst_mir(tcx, ty::EarlyBinder::bind(&f.literal.ty()));
self.cost += if let ty::FnDef(def_id, _) = *fn_ty.kind() && tcx.is_intrinsic(def_id) {
// Don't give intrinsics the extra penalty for calls
INSTR_COST
@@ -839,22 +843,20 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
/// to normalization failure.
fn visit_projection_elem(
&mut self,
- local: Local,
- proj_base: &[PlaceElem<'tcx>],
+ place_ref: PlaceRef<'tcx>,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
) {
if let ProjectionElem::Field(f, ty) = elem {
- let parent = Place { local, projection: self.tcx.mk_place_elems(proj_base) };
- let parent_ty = parent.ty(&self.callee_body.local_decls, self.tcx);
+ let parent_ty = place_ref.ty(&self.callee_body.local_decls, self.tcx);
let check_equal = |this: &mut Self, f_ty| {
// Fast path if there is nothing to substitute.
if ty == f_ty {
return;
}
- let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder(&ty));
- let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder(&f_ty));
+ let ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&ty));
+ let f_ty = this.instance.subst_mir(this.tcx, ty::EarlyBinder::bind(&f_ty));
if ty == f_ty {
return;
}
@@ -935,7 +937,7 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
}
}
- self.super_projection_elem(local, proj_base, elem, context, location);
+ self.super_projection_elem(place_ref, elem, context, location);
}
}
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 1ccf06f61..8a10445f8 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -47,7 +47,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
let Ok(substs) = caller.try_subst_mir_and_normalize_erasing_regions(
tcx,
param_env,
- ty::EarlyBinder(substs),
+ ty::EarlyBinder::bind(substs),
) else {
trace!(?caller, ?param_env, ?substs, "cannot normalize, skipping");
continue;
diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs
index 430a6f6ce..8ed4706e1 100644
--- a/compiler/rustc_mir_transform/src/large_enums.rs
+++ b/compiler/rustc_mir_transform/src/large_enums.rs
@@ -141,7 +141,7 @@ impl EnumSizeOpt {
self.candidate(tcx, param_env, ty, &mut alloc_cache)?;
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
- let tmp_ty = tcx.mk_array(tcx.types.usize, num_variants as u64);
+ let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64);
let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span));
let store_live = Statement {
@@ -208,8 +208,9 @@ impl EnumSizeOpt {
))),
};
- let dst =
- Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span)));
+ let dst = Place::from(
+ local_decls.push(LocalDecl::new(Ty::new_mut_ptr(tcx, ty), span)),
+ );
let dst_ptr = Statement {
source_info,
@@ -219,7 +220,7 @@ impl EnumSizeOpt {
))),
};
- let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8);
+ let dst_cast_ty = Ty::new_mut_ptr(tcx, tcx.types.u8);
let dst_cast_place =
Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span)));
@@ -231,8 +232,9 @@ impl EnumSizeOpt {
))),
};
- let src =
- Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span)));
+ let src = Place::from(
+ local_decls.push(LocalDecl::new(Ty::new_imm_ptr(tcx, ty), span)),
+ );
let src_ptr = Statement {
source_info,
@@ -242,7 +244,7 @@ impl EnumSizeOpt {
))),
};
- let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8);
+ let src_cast_ty = Ty::new_imm_ptr(tcx, tcx.types.u8);
let src_cast_place =
Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span)));
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 65864dc01..fa8257cf9 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -2,7 +2,7 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![feature(box_patterns)]
-#![feature(drain_filter)]
+#![feature(is_sorted)]
#![feature(let_chains)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
@@ -30,8 +30,8 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_index::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{
- traversal, AnalysisPhase, Body, ClearCrossCrate, ConstQualifs, Constant, LocalDecl, MirPass,
- MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
+ traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstQualifs, Constant, LocalDecl,
+ MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
Statement, StatementKind, TerminatorKind, START_BLOCK,
};
use rustc_middle::query::Providers;
@@ -84,6 +84,7 @@ mod match_branches;
mod multiple_return_terminators;
mod normalize_array_len;
mod nrvo;
+mod prettify;
mod ref_prop;
mod remove_noop_landing_pads;
mod remove_storage_markers;
@@ -188,7 +189,7 @@ fn remap_mir_for_const_eval_select<'tcx>(
};
method(place)
}).collect();
- terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, unwind, from_hir_call: false, fn_span };
+ terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, unwind, call_source: CallSource::Misc, fn_span };
}
_ => {}
}
@@ -557,10 +558,13 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
// inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
&multiple_return_terminators::MultipleReturnTerminators,
&instsimplify::InstSimplify,
- &separate_const_switch::SeparateConstSwitch,
&simplify::SimplifyLocals::BeforeConstProp,
&copy_prop::CopyProp,
&ref_prop::ReferencePropagation,
+ // Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may
+ // destroy the SSA property. It should still happen before const-propagation, so the
+ // latter pass will leverage the created opportunities.
+ &separate_const_switch::SeparateConstSwitch,
&const_prop::ConstProp,
&dataflow_const_prop::DataflowConstProp,
//
@@ -581,6 +585,9 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&large_enums::EnumSizeOpt { discrepancy: 128 },
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
&add_call_guards::CriticalCallEdges,
+ // Cleanup for human readability, off by default.
+ &prettify::ReorderBasicBlocks,
+ &prettify::ReorderLocals,
// Dump the end result for testing and debugging purposes.
&dump_mir::Marker("PreCodegen"),
],
diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
index dae01e41e..ce98e9b0c 100644
--- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs
+++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs
@@ -82,30 +82,45 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
drop(args);
terminator.kind = TerminatorKind::Goto { target };
}
- sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
- if let Some(target) = *target {
- let lhs;
- let rhs;
- {
- let mut args = args.drain(..);
- lhs = args.next().unwrap();
- rhs = args.next().unwrap();
- }
- let bin_op = match intrinsic_name {
- sym::wrapping_add => BinOp::Add,
- sym::wrapping_sub => BinOp::Sub,
- sym::wrapping_mul => BinOp::Mul,
- _ => bug!("unexpected intrinsic"),
- };
- block.statements.push(Statement {
- source_info: terminator.source_info,
- kind: StatementKind::Assign(Box::new((
- *destination,
- Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
- ))),
- });
- terminator.kind = TerminatorKind::Goto { target };
+ sym::wrapping_add
+ | sym::wrapping_sub
+ | sym::wrapping_mul
+ | sym::unchecked_add
+ | sym::unchecked_sub
+ | sym::unchecked_mul
+ | sym::unchecked_div
+ | sym::unchecked_rem
+ | sym::unchecked_shl
+ | sym::unchecked_shr => {
+ let target = target.unwrap();
+ let lhs;
+ let rhs;
+ {
+ let mut args = args.drain(..);
+ lhs = args.next().unwrap();
+ rhs = args.next().unwrap();
}
+ let bin_op = match intrinsic_name {
+ sym::wrapping_add => BinOp::Add,
+ sym::wrapping_sub => BinOp::Sub,
+ sym::wrapping_mul => BinOp::Mul,
+ sym::unchecked_add => BinOp::AddUnchecked,
+ sym::unchecked_sub => BinOp::SubUnchecked,
+ sym::unchecked_mul => BinOp::MulUnchecked,
+ sym::unchecked_div => BinOp::Div,
+ sym::unchecked_rem => BinOp::Rem,
+ sym::unchecked_shl => BinOp::ShlUnchecked,
+ sym::unchecked_shr => BinOp::ShrUnchecked,
+ _ => bug!("unexpected intrinsic"),
+ };
+ block.statements.push(Statement {
+ source_info: terminator.source_info,
+ kind: StatementKind::Assign(Box::new((
+ *destination,
+ Rvalue::BinaryOp(bin_op, Box::new((lhs, rhs))),
+ ))),
+ });
+ terminator.kind = TerminatorKind::Goto { target };
}
sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
if let Some(target) = *target {
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 6e40dfa0d..b7cc0db95 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -54,7 +54,7 @@ fn lower_slice_len_call<'tcx>(
args,
destination,
target: Some(bb),
- from_hir_call: true,
+ call_source: CallSource::Normal,
..
} => {
// some heuristics for fast rejection
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 59942dc76..6eb484982 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -41,7 +41,7 @@ pub struct MatchBranchSimplification;
impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
- sess.mir_opt_level() >= 3
+ sess.mir_opt_level() >= 1
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@@ -62,7 +62,12 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
..
} if targets.iter().len() == 1 => {
let (value, target) = targets.iter().next().unwrap();
- if target == targets.otherwise() {
+ // We require that this block and the two possible target blocks all be
+ // distinct.
+ if target == targets.otherwise()
+ || bb_idx == target
+ || bb_idx == targets.otherwise()
+ {
continue;
}
(discr, value, target, targets.otherwise())
diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs
index 3d61d33ce..6c3b7c58f 100644
--- a/compiler/rustc_mir_transform/src/normalize_array_len.rs
+++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs
@@ -41,7 +41,7 @@ fn compute_slice_length<'tcx>(
for (local, rvalue, _) in ssa.assignments(body) {
match rvalue {
Rvalue::Cast(
- CastKind::Pointer(ty::adjustment::PointerCast::Unsize),
+ CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
operand,
cast_ty,
) => {
diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs
new file mode 100644
index 000000000..745fa3084
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/prettify.rs
@@ -0,0 +1,150 @@
+//! These two passes provide no value to the compiler, so are off at every level.
+//!
+//! However, they can be enabled on the command line
+//! (`-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals`)
+//! to make the MIR easier to read for humans.
+
+use crate::MirPass;
+use rustc_index::{bit_set::BitSet, IndexSlice, IndexVec};
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::Session;
+
+/// Rearranges the basic blocks into a *reverse post-order*.
+///
+/// Thus after this pass, all the successors of a block are later than it in the
+/// `IndexVec`, unless that successor is a back-edge (such as from a loop).
+pub struct ReorderBasicBlocks;
+
+impl<'tcx> MirPass<'tcx> for ReorderBasicBlocks {
+ fn is_enabled(&self, _session: &Session) -> bool {
+ false
+ }
+
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let rpo: IndexVec<BasicBlock, BasicBlock> =
+ body.basic_blocks.reverse_postorder().iter().copied().collect();
+ if rpo.iter().is_sorted() {
+ return;
+ }
+
+ let mut updater = BasicBlockUpdater { map: rpo.invert_bijective_mapping(), tcx };
+ debug_assert_eq!(updater.map[START_BLOCK], START_BLOCK);
+ updater.visit_body(body);
+
+ permute(body.basic_blocks.as_mut(), &updater.map);
+ }
+}
+
+/// Rearranges the locals into *use* order.
+///
+/// Thus after this pass, a local with a smaller [`Location`] where it was first
+/// assigned or referenced will have a smaller number.
+///
+/// (Does not reorder arguments nor the [`RETURN_PLACE`].)
+pub struct ReorderLocals;
+
+impl<'tcx> MirPass<'tcx> for ReorderLocals {
+ fn is_enabled(&self, _session: &Session) -> bool {
+ false
+ }
+
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let mut finder =
+ LocalFinder { map: IndexVec::new(), seen: BitSet::new_empty(body.local_decls.len()) };
+
+ // We can't reorder the return place or the arguments
+ for local in (0..=body.arg_count).map(Local::from_usize) {
+ finder.track(local);
+ }
+
+ for (bb, bbd) in body.basic_blocks.iter_enumerated() {
+ finder.visit_basic_block_data(bb, bbd);
+ }
+
+ // track everything in case there are some locals that we never saw,
+ // such as in non-block things like debug info or in non-uses.
+ for local in body.local_decls.indices() {
+ finder.track(local);
+ }
+
+ if finder.map.iter().is_sorted() {
+ return;
+ }
+
+ let mut updater = LocalUpdater { map: finder.map.invert_bijective_mapping(), tcx };
+
+ for local in (0..=body.arg_count).map(Local::from_usize) {
+ debug_assert_eq!(updater.map[local], local);
+ }
+
+ updater.visit_body_preserves_cfg(body);
+
+ permute(&mut body.local_decls, &updater.map);
+ }
+}
+
+fn permute<I: rustc_index::Idx + Ord, T>(data: &mut IndexVec<I, T>, map: &IndexSlice<I, I>) {
+ // FIXME: It would be nice to have a less-awkward way to apply permutations,
+ // but I don't know one that exists. `sort_by_cached_key` has logic for it
+ // internally, but not in a way that we're allowed to use here.
+ let mut enumerated: Vec<_> = std::mem::take(data).into_iter_enumerated().collect();
+ enumerated.sort_by_key(|p| map[p.0]);
+ *data = enumerated.into_iter().map(|p| p.1).collect();
+}
+
+struct BasicBlockUpdater<'tcx> {
+ map: IndexVec<BasicBlock, BasicBlock>,
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for BasicBlockUpdater<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, _location: Location) {
+ for succ in terminator.successors_mut() {
+ *succ = self.map[*succ];
+ }
+ }
+}
+
+struct LocalFinder {
+ map: IndexVec<Local, Local>,
+ seen: BitSet<Local>,
+}
+
+impl LocalFinder {
+ fn track(&mut self, l: Local) {
+ if self.seen.insert(l) {
+ self.map.push(l);
+ }
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for LocalFinder {
+ fn visit_local(&mut self, l: Local, context: PlaceContext, _location: Location) {
+ // Exclude non-uses to keep `StorageLive` from controlling where we put
+ // a `Local`, since it might not actually be assigned until much later.
+ if context.is_use() {
+ self.track(l);
+ }
+ }
+}
+
+struct LocalUpdater<'tcx> {
+ pub map: IndexVec<Local, Local>,
+ pub tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
+ *l = self.map[*l];
+ }
+}
diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
index 1f9e521d3..283931de0 100644
--- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs
@@ -22,7 +22,7 @@ pub struct RemoveUninitDrops;
impl<'tcx> MirPass<'tcx> for RemoveUninitDrops {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let param_env = tcx.param_env(body.source.def_id());
- let Ok((_,move_data)) = MoveData::gather_moves(body, tcx, param_env) else {
+ let Ok(move_data) = MoveData::gather_moves(body, tcx, param_env) else {
// We could continue if there are move errors, but there's not much point since our
// init data isn't complete.
return;
diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs
index 0ea8f2ba9..243cb4635 100644
--- a/compiler/rustc_mir_transform/src/required_consts.rs
+++ b/compiler/rustc_mir_transform/src/required_consts.rs
@@ -17,8 +17,8 @@ impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
let literal = constant.literal;
match literal {
ConstantKind::Ty(c) => match c.kind() {
- ConstKind::Param(_) | ConstKind::Error(_) => {}
- _ => bug!("only ConstKind::Param should be encountered here, got {:#?}", c),
+ ConstKind::Param(_) | ConstKind::Error(_) | ConstKind::Value(_) => {}
+ _ => bug!("only ConstKind::Param/Value should be encountered here, got {:#?}", c),
},
ConstantKind::Unevaluated(..) => self.required_consts.push(*constant),
ConstantKind::Val(..) => {}
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 2479856b7..1d8e54cdc 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -46,7 +46,9 @@ pub struct SeparateConstSwitch;
impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
- sess.mir_opt_level() >= 4
+ // This pass participates in some as-of-yet untested unsoundness found
+ // in https://github.com/rust-lang/rust/issues/112460
+ sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 0eb27c231..b176db3c9 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -32,13 +32,15 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
let mut result = match instance {
ty::InstanceDef::Item(..) => bug!("item {:?} passed to make_shim", instance),
ty::InstanceDef::VTableShim(def_id) => {
- build_call_shim(tcx, instance, Some(Adjustment::Deref), CallKind::Direct(def_id))
+ let adjustment = Adjustment::Deref { source: DerefSource::MutPtr };
+ build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id))
}
ty::InstanceDef::FnPtrShim(def_id, ty) => {
let trait_ = tcx.trait_of_item(def_id).unwrap();
let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) {
Some(ty::ClosureKind::FnOnce) => Adjustment::Identity,
- Some(ty::ClosureKind::FnMut | ty::ClosureKind::Fn) => Adjustment::Deref,
+ Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef },
+ Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef },
None => bug!("fn pointer {:?} is not an fn", ty),
};
@@ -69,7 +71,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
// of this function. Is this intentional?
if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
- let body = EarlyBinder(body.clone()).subst(tcx, substs);
+ let body = EarlyBinder::bind(body.clone()).subst(tcx, substs);
debug!("make_shim({:?}) = {:?}", instance, body);
return body;
}
@@ -108,15 +110,25 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
}
#[derive(Copy, Clone, Debug, PartialEq)]
+enum DerefSource {
+ /// `fn shim(&self) { inner(*self )}`.
+ ImmRef,
+ /// `fn shim(&mut self) { inner(*self )}`.
+ MutRef,
+ /// `fn shim(*mut self) { inner(*self )}`.
+ MutPtr,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
enum Adjustment {
/// Pass the receiver as-is.
Identity,
- /// We get passed `&[mut] self` and call the target with `*self`.
+ /// We get passed a reference or a raw pointer to `self` and call the target with `*self`.
///
/// This either copies `self` (if `Self: Copy`, eg. for function items), or moves out of it
/// (for `VTableShim`, which effectively is passed `&own Self`).
- Deref,
+ Deref { source: DerefSource },
/// We get passed `self: Self` and call the target with `&mut self`.
///
@@ -188,7 +200,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
// has been put into the body.
let reborrow = Rvalue::Ref(
tcx.lifetimes.re_erased,
- BorrowKind::Mut { allow_two_phase_borrow: false },
+ BorrowKind::Mut { kind: MutBorrowKind::Default },
tcx.mk_place_deref(dropee_ptr),
);
let ref_ty = reborrow.ty(body.local_decls(), tcx);
@@ -473,7 +485,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
let tcx = self.tcx;
// `func == Clone::clone(&ty) -> ty`
- let func_ty = tcx.mk_fn_def(self.def_id, [ty]);
+ let func_ty = Ty::new_fn_def(tcx, self.def_id, [ty]);
let func = Operand::Constant(Box::new(Constant {
span: self.span,
user_ty: None,
@@ -482,7 +494,11 @@ impl<'tcx> CloneShimBuilder<'tcx> {
let ref_loc = self.make_place(
Mutability::Not,
- tcx.mk_ref(tcx.lifetimes.re_erased, ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }),
+ Ty::new_ref(
+ tcx,
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
+ ),
);
// `let ref_loc: &ty = &src;`
@@ -500,7 +516,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
destination: dest,
target: Some(next),
unwind: UnwindAction::Cleanup(cleanup),
- from_hir_call: true,
+ call_source: CallSource::Normal,
fn_span: self.span,
},
false,
@@ -632,7 +648,7 @@ fn build_call_shim<'tcx>(
let untuple_args = sig.inputs();
// Create substitutions for the `Self` and `Args` generic parameters of the shim body.
- let arg_tup = tcx.mk_tup(untuple_args);
+ let arg_tup = Ty::new_tup(tcx, untuple_args);
(Some([ty.into(), arg_tup.into()]), Some(untuple_args))
} else {
@@ -644,8 +660,11 @@ fn build_call_shim<'tcx>(
let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig));
assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
- let mut sig =
- if let Some(sig_substs) = sig_substs { sig.subst(tcx, &sig_substs) } else { sig.0 };
+ let mut sig = if let Some(sig_substs) = sig_substs {
+ sig.subst(tcx, &sig_substs)
+ } else {
+ sig.subst_identity()
+ };
if let CallKind::Indirect(fnty) = call_kind {
// `sig` determines our local decls, and thus the callee type in the `Call` terminator. This
@@ -664,8 +683,12 @@ fn build_call_shim<'tcx>(
let self_arg = &mut inputs_and_output[0];
*self_arg = match rcvr_adjustment.unwrap() {
Adjustment::Identity => fnty,
- Adjustment::Deref => tcx.mk_imm_ptr(fnty),
- Adjustment::RefMut => tcx.mk_mut_ptr(fnty),
+ Adjustment::Deref { source } => match source {
+ DerefSource::ImmRef => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, fnty),
+ DerefSource::MutRef => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fnty),
+ DerefSource::MutPtr => Ty::new_mut_ptr(tcx, fnty),
+ },
+ Adjustment::RefMut => bug!("`RefMut` is never used with indirect calls: {instance:?}"),
};
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
}
@@ -677,7 +700,7 @@ fn build_call_shim<'tcx>(
let mut inputs_and_output = sig.inputs_and_output.to_vec();
let self_arg = &mut inputs_and_output[0];
debug_assert!(tcx.generics_of(def_id).has_self && *self_arg == tcx.types.self_param);
- *self_arg = tcx.mk_mut_ptr(*self_arg);
+ *self_arg = Ty::new_mut_ptr(tcx, *self_arg);
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
}
@@ -696,12 +719,13 @@ fn build_call_shim<'tcx>(
let rcvr = rcvr_adjustment.map(|rcvr_adjustment| match rcvr_adjustment {
Adjustment::Identity => Operand::Move(rcvr_place()),
- Adjustment::Deref => Operand::Move(tcx.mk_place_deref(rcvr_place())),
+ Adjustment::Deref { source: _ } => Operand::Move(tcx.mk_place_deref(rcvr_place())),
Adjustment::RefMut => {
// let rcvr = &mut rcvr;
let ref_rcvr = local_decls.push(
LocalDecl::new(
- tcx.mk_ref(
+ Ty::new_ref(
+ tcx,
tcx.lifetimes.re_erased,
ty::TypeAndMut { ty: sig.inputs()[0], mutbl: hir::Mutability::Mut },
),
@@ -709,7 +733,7 @@ fn build_call_shim<'tcx>(
)
.immutable(),
);
- let borrow_kind = BorrowKind::Mut { allow_two_phase_borrow: false };
+ let borrow_kind = BorrowKind::Mut { kind: MutBorrowKind::Default };
statements.push(Statement {
source_info,
kind: StatementKind::Assign(Box::new((
@@ -786,7 +810,7 @@ fn build_call_shim<'tcx>(
} else {
UnwindAction::Continue
},
- from_hir_call: true,
+ call_source: CallSource::Misc,
fn_span: span,
},
false,
@@ -927,7 +951,7 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t
let rvalue = Rvalue::Cast(
CastKind::FnPtrToPtr,
Operand::Move(Place::from(Local::new(1))),
- tcx.mk_imm_ptr(tcx.types.unit),
+ Ty::new_imm_ptr(tcx, tcx.types.unit),
);
let stmt = Statement {
source_info,
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index 2d7729129..94e1da8e1 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -6,7 +6,7 @@ use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields};
-use rustc_target::abi::FieldIdx;
+use rustc_target::abi::{FieldIdx, ReprFlags, FIRST_VARIANT};
pub struct ScalarReplacementOfAggregates;
@@ -18,11 +18,17 @@ impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
#[instrument(level = "debug", skip(self, tcx, body))]
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!(def_id = ?body.source.def_id());
+
+ // Avoid query cycles (generators require optimized MIR for layout).
+ if tcx.type_of(body.source.def_id()).subst_identity().is_generator() {
+ return;
+ }
+
let mut excluded = excluded_locals(body);
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
loop {
debug!(?excluded);
- let escaping = escaping_locals(&excluded, body);
+ let escaping = escaping_locals(tcx, param_env, &excluded, body);
debug!(?escaping);
let replacements = compute_flattening(tcx, param_env, body, escaping);
debug!(?replacements);
@@ -48,11 +54,45 @@ impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
/// - the locals is a union or an enum;
/// - the local's address is taken, and thus the relative addresses of the fields are observable to
/// client code.
-fn escaping_locals(excluded: &BitSet<Local>, body: &Body<'_>) -> BitSet<Local> {
+fn escaping_locals<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ excluded: &BitSet<Local>,
+ body: &Body<'tcx>,
+) -> BitSet<Local> {
+ let is_excluded_ty = |ty: Ty<'tcx>| {
+ if ty.is_union() || ty.is_enum() {
+ return true;
+ }
+ if let ty::Adt(def, _substs) = ty.kind() {
+ if def.repr().flags.contains(ReprFlags::IS_SIMD) {
+ // Exclude #[repr(simd)] types so that they are not de-optimized into an array
+ return true;
+ }
+ // We already excluded unions and enums, so this ADT must have one variant
+ let variant = def.variant(FIRST_VARIANT);
+ if variant.fields.len() > 1 {
+ // If this has more than one field, it cannot be a wrapper that only provides a
+ // niche, so we do not want to automatically exclude it.
+ return false;
+ }
+ let Ok(layout) = tcx.layout_of(param_env.and(ty)) else {
+ // We can't get the layout
+ return true;
+ };
+ if layout.layout.largest_niche().is_some() {
+ // This type has a niche
+ return true;
+ }
+ }
+ // Default for non-ADTs
+ false
+ };
+
let mut set = BitSet::new_empty(body.local_decls.len());
set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
for (local, decl) in body.local_decls().iter_enumerated() {
- if decl.ty.is_union() || decl.ty.is_enum() || excluded.contains(local) {
+ if excluded.contains(local) || is_excluded_ty(decl.ty) {
set.insert(local);
}
}
@@ -396,13 +436,12 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
let mut new_fragments = Vec::new();
debug!(?fragments);
- fragments
- .drain_filter(|fragment| {
- if let Some(repl) =
+ fragments.retain_mut(|fragment| {
+ if let Some(repl) =
self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
{
fragment.contents = repl;
- false
+ true
} else if let Some(local) = fragment.contents.as_local()
&& let Some(frg) = self.gather_debug_info_fragments(local)
{
@@ -410,12 +449,11 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
f.projection.splice(0..0, fragment.projection.iter().copied());
f
}));
- true
- } else {
false
+ } else {
+ true
}
- })
- .for_each(drop);
+ });
debug!(?fragments);
debug!(?new_fragments);
fragments.extend(new_fragments);
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index 7a0d3a025..8dc2dfe13 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -271,6 +271,14 @@ fn compute_copy_classes(ssa: &mut SsaLocals, body: &Body<'_>) {
else { continue };
let Some(rhs) = place.as_local() else { continue };
+ let local_ty = body.local_decls()[local].ty;
+ let rhs_ty = body.local_decls()[rhs].ty;
+ if local_ty != rhs_ty {
+ // FIXME(#112651): This can be removed afterwards.
+ trace!("skipped `{local:?} = {rhs:?}` due to subtyping: {local_ty} != {rhs_ty}");
+ continue;
+ }
+
if !ssa.is_ssa(rhs) {
continue;
}
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 35b154b7b..242269e9d 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -35,15 +35,15 @@
//!
//! - A "mono item" is something that results in a function or global in
//! the LLVM IR of a codegen unit. Mono items do not stand on their
-//! own, they can reference other mono items. For example, if function
+//! own, they can use other mono items. For example, if function
//! `foo()` calls function `bar()` then the mono item for `foo()`
-//! references the mono item for function `bar()`. In general, the
-//! definition for mono item A referencing a mono item B is that
-//! the LLVM artifact produced for A references the LLVM artifact produced
+//! uses the mono item for function `bar()`. In general, the
+//! definition for mono item A using a mono item B is that
+//! the LLVM artifact produced for A uses the LLVM artifact produced
//! for B.
//!
-//! - Mono items and the references between them form a directed graph,
-//! where the mono items are the nodes and references form the edges.
+//! - Mono items and the uses between them form a directed graph,
+//! where the mono items are the nodes and uses form the edges.
//! Let's call this graph the "mono item graph".
//!
//! - The mono item graph for a program contains all mono items
@@ -53,12 +53,11 @@
//! mono item graph for the current crate. It runs in two phases:
//!
//! 1. Discover the roots of the graph by traversing the HIR of the crate.
-//! 2. Starting from the roots, find neighboring nodes by inspecting the MIR
+//! 2. Starting from the roots, find uses by inspecting the MIR
//! representation of the item corresponding to a given node, until no more
//! new nodes are found.
//!
//! ### Discovering roots
-//!
//! The roots of the mono item graph correspond to the public non-generic
//! syntactic items in the source code. We find them by walking the HIR of the
//! crate, and whenever we hit upon a public function, method, or static item,
@@ -69,25 +68,23 @@
//! specified. Functions marked `#[no_mangle]` and functions called by inlinable
//! functions also always act as roots.)
//!
-//! ### Finding neighbor nodes
-//! Given a mono item node, we can discover neighbors by inspecting its
-//! MIR. We walk the MIR and any time we hit upon something that signifies a
-//! reference to another mono item, we have found a neighbor. Since the
-//! mono item we are currently at is always monomorphic, we also know the
-//! concrete type arguments of its neighbors, and so all neighbors again will be
-//! monomorphic. The specific forms a reference to a neighboring node can take
-//! in MIR are quite diverse. Here is an overview:
+//! ### Finding uses
+//! Given a mono item node, we can discover uses by inspecting its MIR. We walk
+//! the MIR to find other mono items used by each mono item. Since the mono
+//! item we are currently at is always monomorphic, we also know the concrete
+//! type arguments of its used mono items. The specific forms a use can take in
+//! MIR are quite diverse. Here is an overview:
//!
//! #### Calling Functions/Methods
-//! The most obvious form of one mono item referencing another is a
+//! The most obvious way for one mono item to use another is a
//! function or method call (represented by a CALL terminator in MIR). But
-//! calls are not the only thing that might introduce a reference between two
+//! calls are not the only thing that might introduce a use between two
//! function mono items, and as we will see below, they are just a
//! specialization of the form described next, and consequently will not get any
//! special treatment in the algorithm.
//!
//! #### Taking a reference to a function or method
-//! A function does not need to actually be called in order to be a neighbor of
+//! A function does not need to actually be called in order to be used by
//! another function. It suffices to just take a reference in order to introduce
//! an edge. Consider the following example:
//!
@@ -109,29 +106,23 @@
//! The MIR of none of these functions will contain an explicit call to
//! `print_val::<i32>`. Nonetheless, in order to mono this program, we need
//! an instance of this function. Thus, whenever we encounter a function or
-//! method in operand position, we treat it as a neighbor of the current
+//! method in operand position, we treat it as a use of the current
//! mono item. Calls are just a special case of that.
//!
//! #### Drop glue
//! Drop glue mono items are introduced by MIR drop-statements. The
-//! generated mono item will again have drop-glue item neighbors if the
+//! generated mono item will have additional drop-glue item uses if the
//! type to be dropped contains nested values that also need to be dropped. It
-//! might also have a function item neighbor for the explicit `Drop::drop`
+//! might also have a function item use for the explicit `Drop::drop`
//! implementation of its type.
//!
//! #### Unsizing Casts
-//! A subtle way of introducing neighbor edges is by casting to a trait object.
+//! A subtle way of introducing use edges is by casting to a trait object.
//! Since the resulting fat-pointer contains a reference to a vtable, we need to
//! instantiate all object-safe methods of the trait, as we need to store
//! pointers to these functions even if they never get called anywhere. This can
//! be seen as a special case of taking a function reference.
//!
-//! #### Boxes
-//! Since `Box` expression have special compiler support, no explicit calls to
-//! `exchange_malloc()` and `box_free()` may show up in MIR, even if the
-//! compiler will generate them. We have to observe `Rvalue::Box` expressions
-//! and Box-typed drop-statements for that purpose.
-//!
//!
//! Interaction with Cross-Crate Inlining
//! -------------------------------------
@@ -151,7 +142,7 @@
//! Mono item collection can be performed in one of two modes:
//!
//! - Lazy mode means that items will only be instantiated when actually
-//! referenced. The goal is to produce the least amount of machine code
+//! used. The goal is to produce the least amount of machine code
//! possible.
//!
//! - Eager mode is meant to be used in conjunction with incremental compilation
@@ -179,14 +170,13 @@ use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::lang_items::LangItem;
-use rustc_index::bit_set::GrowableBitSet;
use rustc_middle::mir::interpret::{AllocId, ConstValue};
use rustc_middle::mir::interpret::{ErrorHandled, GlobalAlloc, Scalar};
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::mir::visit::Visitor as MirVisitor;
use rustc_middle::mir::{self, Local, Location};
use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCast};
+use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
use rustc_middle::ty::{
@@ -199,7 +189,6 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
use rustc_session::Limit;
use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
use rustc_target::abi::Size;
-use std::ops::Range;
use std::path::PathBuf;
use crate::errors::{
@@ -212,114 +201,51 @@ pub enum MonoItemCollectionMode {
Lazy,
}
-/// Maps every mono item to all mono items it references in its
-/// body.
-pub struct InliningMap<'tcx> {
- // Maps a source mono item to the range of mono items
- // accessed by it.
- // The range selects elements within the `targets` vecs.
- index: FxHashMap<MonoItem<'tcx>, Range<usize>>,
- targets: Vec<MonoItem<'tcx>>,
-
- // Contains one bit per mono item in the `targets` field. That bit
- // is true if that mono item needs to be inlined into every CGU.
- inlines: GrowableBitSet<usize>,
-}
-
-/// Struct to store mono items in each collecting and if they should
-/// be inlined. We call `instantiation_mode` to get their inlining
-/// status when inserting new elements, which avoids calling it in
-/// `inlining_map.lock_mut()`. See the `collect_items_rec` implementation
-/// below.
-struct MonoItems<'tcx> {
- // If this is false, we do not need to compute whether items
- // will need to be inlined.
- compute_inlining: bool,
-
- // The TyCtxt used to determine whether the a item should
- // be inlined.
- tcx: TyCtxt<'tcx>,
+pub struct UsageMap<'tcx> {
+ // Maps every mono item to the mono items used by it.
+ used_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
- // The collected mono items. The bool field in each element
- // indicates whether this element should be inlined.
- items: Vec<(Spanned<MonoItem<'tcx>>, bool /*inlined*/)>,
+ // Maps every mono item to the mono items that use it.
+ user_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>>,
}
-impl<'tcx> MonoItems<'tcx> {
- #[inline]
- fn push(&mut self, item: Spanned<MonoItem<'tcx>>) {
- self.extend([item]);
- }
-
- #[inline]
- fn extend<T: IntoIterator<Item = Spanned<MonoItem<'tcx>>>>(&mut self, iter: T) {
- self.items.extend(iter.into_iter().map(|mono_item| {
- let inlined = if !self.compute_inlining {
- false
- } else {
- mono_item.node.instantiation_mode(self.tcx) == InstantiationMode::LocalCopy
- };
- (mono_item, inlined)
- }))
- }
-}
+type MonoItems<'tcx> = Vec<Spanned<MonoItem<'tcx>>>;
-impl<'tcx> InliningMap<'tcx> {
- fn new() -> InliningMap<'tcx> {
- InliningMap {
- index: FxHashMap::default(),
- targets: Vec::new(),
- inlines: GrowableBitSet::with_capacity(1024),
- }
+impl<'tcx> UsageMap<'tcx> {
+ fn new() -> UsageMap<'tcx> {
+ UsageMap { used_map: FxHashMap::default(), user_map: FxHashMap::default() }
}
- fn record_accesses<'a>(
+ fn record_used<'a>(
&mut self,
- source: MonoItem<'tcx>,
- new_targets: &'a [(Spanned<MonoItem<'tcx>>, bool)],
+ user_item: MonoItem<'tcx>,
+ used_items: &'a [Spanned<MonoItem<'tcx>>],
) where
'tcx: 'a,
{
- let start_index = self.targets.len();
- let new_items_count = new_targets.len();
- let new_items_count_total = new_items_count + self.targets.len();
-
- self.targets.reserve(new_items_count);
- self.inlines.ensure(new_items_count_total);
-
- for (i, (Spanned { node: mono_item, .. }, inlined)) in new_targets.into_iter().enumerate() {
- self.targets.push(*mono_item);
- if *inlined {
- self.inlines.insert(i + start_index);
- }
+ let used_items: Vec<_> = used_items.iter().map(|item| item.node).collect();
+ for &used_item in used_items.iter() {
+ self.user_map.entry(used_item).or_default().push(user_item);
}
- let end_index = self.targets.len();
- assert!(self.index.insert(source, start_index..end_index).is_none());
+ assert!(self.used_map.insert(user_item, used_items).is_none());
}
- /// Internally iterate over all items referenced by `source` which will be
- /// made available for inlining.
- pub fn with_inlining_candidates<F>(&self, source: MonoItem<'tcx>, mut f: F)
- where
- F: FnMut(MonoItem<'tcx>),
- {
- 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);
- }
- }
- }
+ pub fn get_user_items(&self, item: MonoItem<'tcx>) -> &[MonoItem<'tcx>] {
+ self.user_map.get(&item).map(|items| items.as_slice()).unwrap_or(&[])
}
- /// Internally iterate over all items and the things each accesses.
- pub fn iter_accesses<F>(&self, mut f: F)
+ /// Internally iterate over all inlined items used by `item`.
+ pub fn for_each_inlined_used_item<F>(&self, tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>, mut f: F)
where
- F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]),
+ F: FnMut(MonoItem<'tcx>),
{
- for (&accessor, range) in &self.index {
- f(accessor, &self.targets[range.clone()])
+ let used_items = self.used_map.get(&item).unwrap();
+ for used_item in used_items.iter() {
+ let is_inlined = used_item.instantiation_mode(tcx) == InstantiationMode::LocalCopy;
+ if is_inlined {
+ f(*used_item);
+ }
}
}
}
@@ -328,7 +254,7 @@ impl<'tcx> InliningMap<'tcx> {
pub fn collect_crate_mono_items(
tcx: TyCtxt<'_>,
mode: MonoItemCollectionMode,
-) -> (FxHashSet<MonoItem<'_>>, InliningMap<'_>) {
+) -> (FxHashSet<MonoItem<'_>>, UsageMap<'_>) {
let _prof_timer = tcx.prof.generic_activity("monomorphization_collector");
let roots =
@@ -337,12 +263,12 @@ pub fn collect_crate_mono_items(
debug!("building mono item graph, beginning at roots");
let mut visited = MTLock::new(FxHashSet::default());
- let mut inlining_map = MTLock::new(InliningMap::new());
+ let mut usage_map = MTLock::new(UsageMap::new());
let recursion_limit = tcx.recursion_limit();
{
let visited: MTLockRef<'_, _> = &mut visited;
- let inlining_map: MTLockRef<'_, _> = &mut inlining_map;
+ let usage_map: MTLockRef<'_, _> = &mut usage_map;
tcx.sess.time("monomorphization_collector_graph_walk", || {
par_for_each_in(roots, |root| {
@@ -353,13 +279,13 @@ pub fn collect_crate_mono_items(
visited,
&mut recursion_depths,
recursion_limit,
- inlining_map,
+ usage_map,
);
});
});
}
- (visited.into_inner(), inlining_map.into_inner())
+ (visited.into_inner(), usage_map.into_inner())
}
// Find all non-generic items by walking the HIR. These items serve as roots to
@@ -367,7 +293,7 @@ pub fn collect_crate_mono_items(
#[instrument(skip(tcx, mode), level = "debug")]
fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<'_>> {
debug!("collecting roots");
- let mut roots = MonoItems { compute_inlining: false, tcx, items: Vec::new() };
+ let mut roots = Vec::new();
{
let entry_fn = tcx.entry_fn(());
@@ -393,9 +319,8 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
// whose predicates hold. Luckily, items that aren't instantiable
// can't actually be used, so we can just skip codegenning them.
roots
- .items
.into_iter()
- .filter_map(|(Spanned { node: mono_item, .. }, _)| {
+ .filter_map(|Spanned { node: mono_item, .. }| {
mono_item.is_instantiable(tcx).then_some(mono_item)
})
.collect()
@@ -403,24 +328,23 @@ fn collect_roots(tcx: TyCtxt<'_>, mode: MonoItemCollectionMode) -> Vec<MonoItem<
/// Collect all monomorphized items reachable from `starting_point`, and emit a note diagnostic if a
/// post-monomorphization error is encountered during a collection step.
-#[instrument(skip(tcx, visited, recursion_depths, recursion_limit, inlining_map), level = "debug")]
+#[instrument(skip(tcx, visited, recursion_depths, recursion_limit, usage_map), level = "debug")]
fn collect_items_rec<'tcx>(
tcx: TyCtxt<'tcx>,
- starting_point: Spanned<MonoItem<'tcx>>,
+ starting_item: Spanned<MonoItem<'tcx>>,
visited: MTLockRef<'_, FxHashSet<MonoItem<'tcx>>>,
recursion_depths: &mut DefIdMap<usize>,
recursion_limit: Limit,
- inlining_map: MTLockRef<'_, InliningMap<'tcx>>,
+ usage_map: MTLockRef<'_, UsageMap<'tcx>>,
) {
- if !visited.lock_mut().insert(starting_point.node) {
+ if !visited.lock_mut().insert(starting_item.node) {
// We've been here already, no need to search again.
return;
}
- let mut neighbors = MonoItems { compute_inlining: true, tcx, items: Vec::new() };
+ let mut used_items = Vec::new();
let recursion_depth_reset;
- //
// Post-monomorphization errors MVP
//
// We can encounter errors while monomorphizing an item, but we don't have a good way of
@@ -446,7 +370,7 @@ fn collect_items_rec<'tcx>(
// FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do.
let error_count = tcx.sess.diagnostic().err_count();
- match starting_point.node {
+ match starting_item.node {
MonoItem::Static(def_id) => {
let instance = Instance::mono(tcx, def_id);
@@ -454,19 +378,19 @@ fn collect_items_rec<'tcx>(
debug_assert!(should_codegen_locally(tcx, &instance));
let ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
- visit_drop_use(tcx, ty, true, starting_point.span, &mut neighbors);
+ visit_drop_use(tcx, ty, true, starting_item.span, &mut used_items);
recursion_depth_reset = None;
if let Ok(alloc) = tcx.eval_static_initializer(def_id) {
for &id in alloc.inner().provenance().ptrs().values() {
- collect_miri(tcx, id, &mut neighbors);
+ collect_miri(tcx, id, &mut used_items);
}
}
if tcx.needs_thread_local_shim(def_id) {
- neighbors.push(respan(
- starting_point.span,
+ used_items.push(respan(
+ starting_item.span,
MonoItem::Fn(Instance {
def: InstanceDef::ThreadLocalShim(def_id),
substs: InternalSubsts::empty(),
@@ -482,14 +406,14 @@ fn collect_items_rec<'tcx>(
recursion_depth_reset = Some(check_recursion_limit(
tcx,
instance,
- starting_point.span,
+ starting_item.span,
recursion_depths,
recursion_limit,
));
check_type_length_limit(tcx, instance);
rustc_data_structures::stack::ensure_sufficient_stack(|| {
- collect_neighbours(tcx, instance, &mut neighbors);
+ collect_used_items(tcx, instance, &mut used_items);
});
}
MonoItem::GlobalAsm(item_id) => {
@@ -507,13 +431,13 @@ fn collect_items_rec<'tcx>(
hir::InlineAsmOperand::SymFn { anon_const } => {
let fn_ty =
tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
- visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors);
+ visit_fn_use(tcx, fn_ty, false, *op_sp, &mut used_items);
}
hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
let instance = Instance::mono(tcx, *def_id);
if should_codegen_locally(tcx, &instance) {
trace!("collecting static {:?}", def_id);
- neighbors.push(dummy_spanned(MonoItem::Static(*def_id)));
+ used_items.push(dummy_spanned(MonoItem::Static(*def_id)));
}
}
hir::InlineAsmOperand::In { .. }
@@ -533,19 +457,19 @@ fn collect_items_rec<'tcx>(
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
// mono item graph.
if tcx.sess.diagnostic().err_count() > error_count
- && starting_point.node.is_generic_fn()
- && starting_point.node.is_user_defined()
+ && starting_item.node.is_generic_fn()
+ && starting_item.node.is_user_defined()
{
- let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string());
+ let formatted_item = with_no_trimmed_paths!(starting_item.node.to_string());
tcx.sess.emit_note(EncounteredErrorWhileInstantiating {
- span: starting_point.span,
+ span: starting_item.span,
formatted_item,
});
}
- inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors.items);
+ usage_map.lock_mut().record_used(starting_item.node, &used_items);
- for (neighbour, _) in neighbors.items {
- collect_items_rec(tcx, neighbour, visited, recursion_depths, recursion_limit, inlining_map);
+ for used_item in used_items {
+ collect_items_rec(tcx, used_item, visited, recursion_depths, recursion_limit, usage_map);
}
if let Some((def_id, depth)) = recursion_depth_reset {
@@ -661,14 +585,14 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
}
}
-struct MirNeighborCollector<'a, 'tcx> {
+struct MirUsedCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
body: &'a mir::Body<'tcx>,
output: &'a mut MonoItems<'tcx>,
instance: Instance<'tcx>,
}
-impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> {
+impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
pub fn monomorphize<T>(&self, value: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
@@ -677,12 +601,12 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> {
self.instance.subst_mir_and_normalize_erasing_regions(
self.tcx,
ty::ParamEnv::reveal_all(),
- ty::EarlyBinder(value),
+ ty::EarlyBinder::bind(value),
)
}
}
-impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
+impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) {
debug!("visiting rvalue {:?}", *rvalue);
@@ -693,7 +617,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
// have to instantiate all methods of the trait being cast to, so we
// can build the appropriate vtable.
mir::Rvalue::Cast(
- mir::CastKind::Pointer(PointerCast::Unsize),
+ mir::CastKind::PointerCoercion(PointerCoercion::Unsize),
ref operand,
target_ty,
)
@@ -719,7 +643,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
}
}
mir::Rvalue::Cast(
- mir::CastKind::Pointer(PointerCast::ReifyFnPointer),
+ mir::CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer),
ref operand,
_,
) => {
@@ -728,7 +652,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output);
}
mir::Rvalue::Cast(
- mir::CastKind::Pointer(PointerCast::ClosureFnPointer(_)),
+ mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
ref operand,
_,
) => {
@@ -1442,13 +1366,13 @@ fn collect_miri<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIte
/// Scans the MIR in order to find function calls, closures, and drop-glue.
#[instrument(skip(tcx, output), level = "debug")]
-fn collect_neighbours<'tcx>(
+fn collect_used_items<'tcx>(
tcx: TyCtxt<'tcx>,
instance: Instance<'tcx>,
output: &mut MonoItems<'tcx>,
) {
let body = tcx.instance_mir(instance.def);
- MirNeighborCollector { tcx, body: &body, output, instance }.visit_body(&body);
+ MirUsedCollector { tcx, body: &body, output, instance }.visit_body(&body);
}
#[instrument(skip(tcx, output), level = "debug")]
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index ecc50c3f6..5f05020ac 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -1,4 +1,5 @@
#![feature(array_windows)]
+#![feature(is_sorted)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
@@ -30,12 +31,12 @@ fn custom_coerce_unsize_info<'tcx>(
source_ty: Ty<'tcx>,
target_ty: Ty<'tcx>,
) -> CustomCoerceUnsized {
- let trait_ref = ty::Binder::dummy(ty::TraitRef::from_lang_item(
+ let trait_ref = ty::TraitRef::from_lang_item(
tcx.tcx,
LangItem::CoerceUnsized,
tcx.span,
[source_ty, target_ty],
- ));
+ );
match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData {
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
new file mode 100644
index 000000000..da76cf223
--- /dev/null
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -0,0 +1,1274 @@
+//! Partitioning Codegen Units for Incremental Compilation
+//! ======================================================
+//!
+//! The task of this module is to take the complete set of monomorphizations of
+//! a crate and produce a set of codegen units from it, where a codegen unit
+//! is a named set of (mono-item, linkage) pairs. That is, this module
+//! decides which monomorphization appears in which codegen units with which
+//! linkage. The following paragraphs describe some of the background on the
+//! partitioning scheme.
+//!
+//! The most important opportunity for saving on compilation time with
+//! incremental compilation is to avoid re-codegenning and re-optimizing code.
+//! Since the unit of codegen and optimization for LLVM is "modules" or, how
+//! we call them "codegen units", the particulars of how much time can be saved
+//! by incremental compilation are tightly linked to how the output program is
+//! partitioned into these codegen units prior to passing it to LLVM --
+//! especially because we have to treat codegen units as opaque entities once
+//! they are created: There is no way for us to incrementally update an existing
+//! LLVM module and so we have to build any such module from scratch if it was
+//! affected by some change in the source code.
+//!
+//! From that point of view it would make sense to maximize the number of
+//! codegen units by, for example, putting each function into its own module.
+//! That way only those modules would have to be re-compiled that were actually
+//! affected by some change, minimizing the number of functions that could have
+//! been re-used but just happened to be located in a module that is
+//! re-compiled.
+//!
+//! However, since LLVM optimization does not work across module boundaries,
+//! using such a highly granular partitioning would lead to very slow runtime
+//! code since it would effectively prohibit inlining and other inter-procedure
+//! optimizations. We want to avoid that as much as possible.
+//!
+//! Thus we end up with a trade-off: The bigger the codegen units, the better
+//! LLVM's optimizer can do its work, but also the smaller the compilation time
+//! reduction we get from incremental compilation.
+//!
+//! Ideally, we would create a partitioning such that there are few big codegen
+//! units with few interdependencies between them. For now though, we use the
+//! following heuristic to determine the partitioning:
+//!
+//! - There are two codegen units for every source-level module:
+//! - One for "stable", that is non-generic, code
+//! - One for more "volatile" code, i.e., monomorphized instances of functions
+//! defined in that module
+//!
+//! In order to see why this heuristic makes sense, let's take a look at when a
+//! codegen unit can get invalidated:
+//!
+//! 1. The most straightforward case is when the BODY of a function or global
+//! changes. Then any codegen unit containing the code for that item has to be
+//! re-compiled. Note that this includes all codegen units where the function
+//! has been inlined.
+//!
+//! 2. The next case is when the SIGNATURE of a function or global changes. In
+//! this case, all codegen units containing a REFERENCE to that item have to be
+//! re-compiled. This is a superset of case 1.
+//!
+//! 3. The final and most subtle case is when a REFERENCE to a generic function
+//! is added or removed somewhere. Even though the definition of the function
+//! might be unchanged, a new REFERENCE might introduce a new monomorphized
+//! instance of this function which has to be placed and compiled somewhere.
+//! Conversely, when removing a REFERENCE, it might have been the last one with
+//! that particular set of generic arguments and thus we have to remove it.
+//!
+//! From the above we see that just using one codegen unit per source-level
+//! module is not such a good idea, since just adding a REFERENCE to some
+//! generic item somewhere else would invalidate everything within the module
+//! containing the generic item. The heuristic above reduces this detrimental
+//! side-effect of references a little by at least not touching the non-generic
+//! code of the module.
+//!
+//! A Note on Inlining
+//! ------------------
+//! As briefly mentioned above, in order for LLVM to be able to inline a
+//! function call, the body of the function has to be available in the LLVM
+//! module where the call is made. This has a few consequences for partitioning:
+//!
+//! - The partitioning algorithm has to take care of placing functions into all
+//! codegen units where they should be available for inlining. It also has to
+//! decide on the correct linkage for these functions.
+//!
+//! - The partitioning algorithm has to know which functions are likely to get
+//! inlined, so it can distribute function instantiations accordingly. Since
+//! there is no way of knowing for sure which functions LLVM will decide to
+//! inline in the end, we apply a heuristic here: Only functions marked with
+//! `#[inline]` are considered for inlining by the partitioner. The current
+//! implementation will not try to determine if a function is likely to be
+//! inlined by looking at the functions definition.
+//!
+//! Note though that as a side-effect of creating a codegen units per
+//! source-level module, functions from the same module will be available for
+//! inlining, even when they are not marked `#[inline]`.
+
+use std::cmp;
+use std::collections::hash_map::Entry;
+use std::fs::{self, File};
+use std::io::{BufWriter, Write};
+use std::path::{Path, PathBuf};
+
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::sync;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
+use rustc_hir::definitions::DefPathDataName;
+use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
+use rustc_middle::mir;
+use rustc_middle::mir::mono::{
+ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, Visibility,
+};
+use rustc_middle::query::Providers;
+use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths};
+use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt};
+use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
+use rustc_session::CodegenUnits;
+use rustc_span::symbol::Symbol;
+
+use crate::collector::UsageMap;
+use crate::collector::{self, MonoItemCollectionMode};
+use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode};
+
+struct PartitioningCx<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ usage_map: &'a UsageMap<'tcx>,
+}
+
+struct PlacedMonoItems<'tcx> {
+ /// The codegen units, sorted by name to make things deterministic.
+ codegen_units: Vec<CodegenUnit<'tcx>>,
+
+ internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+
+ /// These must be obtained when the iterator in `partition` runs. They
+ /// can't be obtained later because some inlined functions might not be
+ /// reachable.
+ unique_inlined_stats: (usize, usize),
+}
+
+// The output CGUs are sorted by name.
+fn partition<'tcx, I>(
+ tcx: TyCtxt<'tcx>,
+ mono_items: I,
+ usage_map: &UsageMap<'tcx>,
+) -> Vec<CodegenUnit<'tcx>>
+where
+ I: Iterator<Item = MonoItem<'tcx>>,
+{
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
+
+ let cx = &PartitioningCx { tcx, usage_map };
+
+ // Place all mono items into a codegen unit. `place_mono_items` is
+ // responsible for initializing the CGU size estimates.
+ let PlacedMonoItems { mut codegen_units, internalization_candidates, unique_inlined_stats } = {
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_items");
+ let placed = place_mono_items(cx, mono_items);
+
+ debug_dump(tcx, "PLACE", &placed.codegen_units, placed.unique_inlined_stats);
+
+ placed
+ };
+
+ // Merge until we have at most `max_cgu_count` codegen units.
+ // `merge_codegen_units` is responsible for updating the CGU size
+ // estimates.
+ {
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
+ merge_codegen_units(cx, &mut codegen_units);
+ debug_dump(tcx, "MERGE", &codegen_units, unique_inlined_stats);
+ }
+
+ // Make as many symbols "internal" as possible, so LLVM has more freedom to
+ // optimize.
+ if !tcx.sess.link_dead_code() {
+ let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
+ internalize_symbols(cx, &mut codegen_units, internalization_candidates);
+
+ debug_dump(tcx, "INTERNALIZE", &codegen_units, unique_inlined_stats);
+ }
+
+ // Mark one CGU for dead code, if necessary.
+ let instrument_dead_code =
+ tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions();
+ if instrument_dead_code {
+ mark_code_coverage_dead_code_cgu(&mut codegen_units);
+ }
+
+ // Ensure CGUs are sorted by name, so that we get deterministic results.
+ if !codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str()))) {
+ let mut names = String::new();
+ for cgu in codegen_units.iter() {
+ names += &format!("- {}\n", cgu.name());
+ }
+ bug!("unsorted CGUs:\n{names}");
+ }
+
+ codegen_units
+}
+
+fn place_mono_items<'tcx, I>(cx: &PartitioningCx<'_, 'tcx>, mono_items: I) -> PlacedMonoItems<'tcx>
+where
+ I: Iterator<Item = MonoItem<'tcx>>,
+{
+ let mut codegen_units = FxHashMap::default();
+ let is_incremental_build = cx.tcx.sess.opts.incremental.is_some();
+ let mut internalization_candidates = FxHashSet::default();
+
+ // Determine if monomorphizations instantiated in this crate will be made
+ // available to downstream crates. This depends on whether we are in
+ // share-generics mode and whether the current crate can even have
+ // downstream crates.
+ let export_generics =
+ cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
+
+ let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
+ let cgu_name_cache = &mut FxHashMap::default();
+
+ let mut num_unique_inlined_items = 0;
+ let mut unique_inlined_items_size = 0;
+ for mono_item in mono_items {
+ // Handle only root items directly here. Inlined items are handled at
+ // the bottom of the loop based on reachability.
+ match mono_item.instantiation_mode(cx.tcx) {
+ InstantiationMode::GloballyShared { .. } => {}
+ InstantiationMode::LocalCopy => {
+ num_unique_inlined_items += 1;
+ unique_inlined_items_size += mono_item.size_estimate(cx.tcx);
+ continue;
+ }
+ }
+
+ let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
+ let is_volatile = is_incremental_build && mono_item.is_generic_fn();
+
+ let cgu_name = match characteristic_def_id {
+ Some(def_id) => compute_codegen_unit_name(
+ cx.tcx,
+ cgu_name_builder,
+ def_id,
+ is_volatile,
+ cgu_name_cache,
+ ),
+ None => fallback_cgu_name(cgu_name_builder),
+ };
+
+ let cgu = codegen_units.entry(cgu_name).or_insert_with(|| CodegenUnit::new(cgu_name));
+
+ let mut can_be_internalized = true;
+ let (linkage, visibility) = mono_item_linkage_and_visibility(
+ cx.tcx,
+ &mono_item,
+ &mut can_be_internalized,
+ export_generics,
+ );
+ if visibility == Visibility::Hidden && can_be_internalized {
+ internalization_candidates.insert(mono_item);
+ }
+
+ cgu.items_mut().insert(mono_item, (linkage, visibility));
+
+ // Get all inlined items that are reachable from `mono_item` without
+ // going via another root item. This includes drop-glue, functions from
+ // external crates, and local functions the definition of which is
+ // marked with `#[inline]`.
+ let mut reachable_inlined_items = FxHashSet::default();
+ get_reachable_inlined_items(cx.tcx, mono_item, cx.usage_map, &mut reachable_inlined_items);
+
+ // Add those inlined items. It's possible an inlined item is reachable
+ // from multiple root items within a CGU, which is fine, it just means
+ // the `insert` will be a no-op.
+ for inlined_item in reachable_inlined_items {
+ // This is a CGU-private copy.
+ cgu.items_mut().insert(inlined_item, (Linkage::Internal, Visibility::Default));
+ }
+ }
+
+ // Always ensure we have at least one CGU; otherwise, if we have a
+ // crate with just types (for example), we could wind up with no CGU.
+ if codegen_units.is_empty() {
+ let cgu_name = fallback_cgu_name(cgu_name_builder);
+ codegen_units.insert(cgu_name, CodegenUnit::new(cgu_name));
+ }
+
+ let mut codegen_units: Vec<_> = codegen_units.into_values().collect();
+ codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
+
+ for cgu in codegen_units.iter_mut() {
+ cgu.compute_size_estimate(cx.tcx);
+ }
+
+ return PlacedMonoItems {
+ codegen_units,
+ internalization_candidates,
+ unique_inlined_stats: (num_unique_inlined_items, unique_inlined_items_size),
+ };
+
+ fn get_reachable_inlined_items<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ item: MonoItem<'tcx>,
+ usage_map: &UsageMap<'tcx>,
+ visited: &mut FxHashSet<MonoItem<'tcx>>,
+ ) {
+ usage_map.for_each_inlined_used_item(tcx, item, |inlined_item| {
+ let is_new = visited.insert(inlined_item);
+ if is_new {
+ get_reachable_inlined_items(tcx, inlined_item, usage_map, visited);
+ }
+ });
+ }
+}
+
+// This function requires the CGUs to be sorted by name on input, and ensures
+// they are sorted by name on return, for deterministic behaviour.
+fn merge_codegen_units<'tcx>(
+ cx: &PartitioningCx<'_, 'tcx>,
+ codegen_units: &mut Vec<CodegenUnit<'tcx>>,
+) {
+ assert!(cx.tcx.sess.codegen_units().as_usize() >= 1);
+
+ // A sorted order here ensures merging is deterministic.
+ assert!(codegen_units.is_sorted_by(|a, b| Some(a.name().as_str().cmp(b.name().as_str()))));
+
+ // This map keeps track of what got merged into what.
+ let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> =
+ codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect();
+
+ // Having multiple CGUs can drastically speed up compilation. But for
+ // non-incremental builds, tiny CGUs slow down compilation *and* result in
+ // worse generated code. So we don't allow CGUs smaller than this (unless
+ // there is just one CGU, of course). Note that CGU sizes of 100,000+ are
+ // common in larger programs, so this isn't all that large.
+ const NON_INCR_MIN_CGU_SIZE: usize = 1800;
+
+ // Repeatedly merge the two smallest codegen units as long as:
+ // - we have more CGUs than the upper limit, or
+ // - (Non-incremental builds only) the user didn't specify a CGU count, and
+ // there are multiple CGUs, and some are below the minimum size.
+ //
+ // The "didn't specify a CGU count" condition is because when an explicit
+ // count is requested we observe it as closely as possible. For example,
+ // the `compiler_builtins` crate sets `codegen-units = 10000` and it's
+ // critical they aren't merged. Also, some tests use explicit small values
+ // and likewise won't work if small CGUs are merged.
+ while codegen_units.len() > cx.tcx.sess.codegen_units().as_usize()
+ || (cx.tcx.sess.opts.incremental.is_none()
+ && matches!(cx.tcx.sess.codegen_units(), CodegenUnits::Default(_))
+ && codegen_units.len() > 1
+ && codegen_units.iter().any(|cgu| cgu.size_estimate() < NON_INCR_MIN_CGU_SIZE))
+ {
+ // Sort small cgus to the back.
+ codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
+
+ let mut smallest = codegen_units.pop().unwrap();
+ let second_smallest = codegen_units.last_mut().unwrap();
+
+ // Move the items from `smallest` to `second_smallest`. Some of them
+ // may be duplicate inlined items, in which case the destination CGU is
+ // unaffected. Recalculate size estimates afterwards.
+ second_smallest.items_mut().extend(smallest.items_mut().drain());
+ second_smallest.compute_size_estimate(cx.tcx);
+
+ // Record that `second_smallest` now contains all the stuff that was
+ // in `smallest` before.
+ let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap();
+ cgu_contents.get_mut(&second_smallest.name()).unwrap().append(&mut consumed_cgu_names);
+
+ debug!(
+ "CodegenUnit {} merged into CodegenUnit {}",
+ smallest.name(),
+ second_smallest.name()
+ );
+ }
+
+ let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
+
+ // Rename the newly merged CGUs.
+ if cx.tcx.sess.opts.incremental.is_some() {
+ // If we are doing incremental compilation, we want CGU names to
+ // reflect the path of the source level module they correspond to.
+ // For CGUs that contain the code of multiple modules because of the
+ // merging done above, we use a concatenation of the names of all
+ // contained CGUs.
+ let new_cgu_names: FxHashMap<Symbol, String> = cgu_contents
+ .into_iter()
+ // This `filter` makes sure we only update the name of CGUs that
+ // were actually modified by merging.
+ .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
+ .map(|(current_cgu_name, cgu_contents)| {
+ let mut cgu_contents: Vec<&str> = cgu_contents.iter().map(|s| s.as_str()).collect();
+
+ // Sort the names, so things are deterministic and easy to
+ // predict. We are sorting primitive `&str`s here so we can
+ // use unstable sort.
+ cgu_contents.sort_unstable();
+
+ (current_cgu_name, cgu_contents.join("--"))
+ })
+ .collect();
+
+ for cgu in codegen_units.iter_mut() {
+ if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
+ if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
+ cgu.set_name(Symbol::intern(&new_cgu_name));
+ } else {
+ // If we don't require CGU names to be human-readable,
+ // we use a fixed length hash of the composite CGU name
+ // instead.
+ let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name);
+ cgu.set_name(Symbol::intern(&new_cgu_name));
+ }
+ }
+ }
+
+ // A sorted order here ensures what follows can be deterministic.
+ codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
+ } else {
+ // When compiling non-incrementally, we rename the CGUS so they have
+ // identical names except for the numeric suffix, something like
+ // `regex.f10ba03eb5ec7975-cgu.N`, where `N` varies.
+ //
+ // It is useful for debugging and profiling purposes if the resulting
+ // CGUs are sorted by name *and* reverse sorted by size. (CGU 0 is the
+ // biggest, CGU 1 is the second biggest, etc.)
+ //
+ // So first we reverse sort by size. Then we generate the names with
+ // zero-padded suffixes, which means they are automatically sorted by
+ // names. The numeric suffix width depends on the number of CGUs, which
+ // is always greater than zero:
+ // - [1,9] CGUs: `0`, `1`, `2`, ...
+ // - [10,99] CGUs: `00`, `01`, `02`, ...
+ // - [100,999] CGUs: `000`, `001`, `002`, ...
+ // - etc.
+ //
+ // If we didn't zero-pad the sorted-by-name order would be `XYZ-cgu.0`,
+ // `XYZ-cgu.1`, `XYZ-cgu.10`, `XYZ-cgu.11`, ..., `XYZ-cgu.2`, etc.
+ codegen_units.sort_by_key(|cgu| cmp::Reverse(cgu.size_estimate()));
+ let num_digits = codegen_units.len().ilog10() as usize + 1;
+ for (index, cgu) in codegen_units.iter_mut().enumerate() {
+ // Note: `WorkItem::short_description` depends on this name ending
+ // with `-cgu.` followed by a numeric suffix. Please keep it in
+ // sync with this code.
+ let suffix = format!("{index:0num_digits$}");
+ let numbered_codegen_unit_name =
+ cgu_name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(suffix));
+ cgu.set_name(numbered_codegen_unit_name);
+ }
+ }
+}
+
+fn internalize_symbols<'tcx>(
+ cx: &PartitioningCx<'_, 'tcx>,
+ codegen_units: &mut [CodegenUnit<'tcx>],
+ internalization_candidates: FxHashSet<MonoItem<'tcx>>,
+) {
+ /// For symbol internalization, we need to know whether a symbol/mono-item
+ /// is used from outside the codegen unit it is defined in. This type is
+ /// used to keep track of that.
+ #[derive(Clone, PartialEq, Eq, Debug)]
+ enum MonoItemPlacement {
+ SingleCgu(Symbol),
+ MultipleCgus,
+ }
+
+ let mut mono_item_placements = FxHashMap::default();
+ let single_codegen_unit = codegen_units.len() == 1;
+
+ if !single_codegen_unit {
+ for cgu in codegen_units.iter() {
+ for item in cgu.items().keys() {
+ // If there is more than one codegen unit, we need to keep track
+ // in which codegen units each monomorphization is placed.
+ match mono_item_placements.entry(*item) {
+ Entry::Occupied(e) => {
+ let placement = e.into_mut();
+ debug_assert!(match *placement {
+ MonoItemPlacement::SingleCgu(cgu_name) => cgu_name != cgu.name(),
+ MonoItemPlacement::MultipleCgus => true,
+ });
+ *placement = MonoItemPlacement::MultipleCgus;
+ }
+ Entry::Vacant(e) => {
+ e.insert(MonoItemPlacement::SingleCgu(cgu.name()));
+ }
+ }
+ }
+ }
+ }
+
+ // For each internalization candidates in each codegen unit, check if it is
+ // used from outside its defining codegen unit.
+ for cgu in codegen_units {
+ let home_cgu = MonoItemPlacement::SingleCgu(cgu.name());
+
+ for (item, linkage_and_visibility) in cgu.items_mut() {
+ if !internalization_candidates.contains(item) {
+ // This item is no candidate for internalizing, so skip it.
+ continue;
+ }
+
+ if !single_codegen_unit {
+ debug_assert_eq!(mono_item_placements[item], home_cgu);
+
+ if cx
+ .usage_map
+ .get_user_items(*item)
+ .iter()
+ .filter_map(|user_item| {
+ // Some user mono items might not have been
+ // instantiated. We can safely ignore those.
+ mono_item_placements.get(user_item)
+ })
+ .any(|placement| *placement != home_cgu)
+ {
+ // Found a user from another CGU, so skip to the next item
+ // without marking this one as internal.
+ continue;
+ }
+ }
+
+ // If we got here, we did not find any uses from other CGUs, so
+ // it's fine to make this monomorphization internal.
+ *linkage_and_visibility = (Linkage::Internal, Visibility::Default);
+ }
+ }
+}
+
+fn mark_code_coverage_dead_code_cgu<'tcx>(codegen_units: &mut [CodegenUnit<'tcx>]) {
+ assert!(!codegen_units.is_empty());
+
+ // Find the smallest CGU that has exported symbols and put the dead
+ // function stubs in that CGU. We look for exported symbols to increase
+ // the likelihood the linker won't throw away the dead functions.
+ // FIXME(#92165): In order to truly resolve this, we need to make sure
+ // the object file (CGU) containing the dead function stubs is included
+ // in the final binary. This will probably require forcing these
+ // function symbols to be included via `-u` or `/include` linker args.
+ let dead_code_cgu = codegen_units
+ .iter_mut()
+ .filter(|cgu| cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External))
+ .min_by_key(|cgu| cgu.size_estimate());
+
+ // If there are no CGUs that have externally linked items, then we just
+ // pick the first CGU as a fallback.
+ let dead_code_cgu = if let Some(cgu) = dead_code_cgu { cgu } else { &mut codegen_units[0] };
+
+ dead_code_cgu.make_code_coverage_dead_code_cgu();
+}
+
+fn characteristic_def_id_of_mono_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mono_item: MonoItem<'tcx>,
+) -> Option<DefId> {
+ match mono_item {
+ MonoItem::Fn(instance) => {
+ let def_id = match instance.def {
+ ty::InstanceDef::Item(def) => def,
+ ty::InstanceDef::VTableShim(..)
+ | ty::InstanceDef::ReifyShim(..)
+ | ty::InstanceDef::FnPtrShim(..)
+ | ty::InstanceDef::ClosureOnceShim { .. }
+ | ty::InstanceDef::Intrinsic(..)
+ | ty::InstanceDef::DropGlue(..)
+ | ty::InstanceDef::Virtual(..)
+ | ty::InstanceDef::CloneShim(..)
+ | ty::InstanceDef::ThreadLocalShim(..)
+ | ty::InstanceDef::FnPtrAddrShim(..) => return None,
+ };
+
+ // If this is a method, we want to put it into the same module as
+ // its self-type. If the self-type does not provide a characteristic
+ // DefId, we use the location of the impl after all.
+
+ if tcx.trait_of_item(def_id).is_some() {
+ let self_ty = instance.substs.type_at(0);
+ // This is a default implementation of a trait method.
+ return characteristic_def_id_of_type(self_ty).or(Some(def_id));
+ }
+
+ if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
+ if tcx.sess.opts.incremental.is_some()
+ && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait()
+ {
+ // Put `Drop::drop` into the same cgu as `drop_in_place`
+ // since `drop_in_place` is the only thing that can
+ // call it.
+ return None;
+ }
+
+ // When polymorphization is enabled, methods which do not depend on their generic
+ // parameters, but the self-type of their impl block do will fail to normalize.
+ if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() {
+ // This is a method within an impl, find out what the self-type is:
+ let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
+ instance.substs,
+ ty::ParamEnv::reveal_all(),
+ tcx.type_of(impl_def_id),
+ );
+ if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
+ return Some(def_id);
+ }
+ }
+ }
+
+ Some(def_id)
+ }
+ MonoItem::Static(def_id) => Some(def_id),
+ MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.to_def_id()),
+ }
+}
+
+fn compute_codegen_unit_name(
+ tcx: TyCtxt<'_>,
+ name_builder: &mut CodegenUnitNameBuilder<'_>,
+ def_id: DefId,
+ volatile: bool,
+ cache: &mut CguNameCache,
+) -> Symbol {
+ // Find the innermost module that is not nested within a function.
+ let mut current_def_id = def_id;
+ let mut cgu_def_id = None;
+ // Walk backwards from the item we want to find the module for.
+ loop {
+ if current_def_id.is_crate_root() {
+ if cgu_def_id.is_none() {
+ // If we have not found a module yet, take the crate root.
+ cgu_def_id = Some(def_id.krate.as_def_id());
+ }
+ break;
+ } else if tcx.def_kind(current_def_id) == DefKind::Mod {
+ if cgu_def_id.is_none() {
+ cgu_def_id = Some(current_def_id);
+ }
+ } else {
+ // If we encounter something that is not a module, throw away
+ // any module that we've found so far because we now know that
+ // it is nested within something else.
+ cgu_def_id = None;
+ }
+
+ current_def_id = tcx.parent(current_def_id);
+ }
+
+ let cgu_def_id = cgu_def_id.unwrap();
+
+ *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
+ let def_path = tcx.def_path(cgu_def_id);
+
+ let components = def_path.data.iter().map(|part| match part.data.name() {
+ DefPathDataName::Named(name) => name,
+ DefPathDataName::Anon { .. } => unreachable!(),
+ });
+
+ let volatile_suffix = volatile.then_some("volatile");
+
+ name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
+ })
+}
+
+// Anything we can't find a proper codegen unit for goes into this.
+fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
+ name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
+}
+
+fn mono_item_linkage_and_visibility<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mono_item: &MonoItem<'tcx>,
+ can_be_internalized: &mut bool,
+ export_generics: bool,
+) -> (Linkage, Visibility) {
+ if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
+ return (explicit_linkage, Visibility::Default);
+ }
+ let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
+ (Linkage::External, vis)
+}
+
+type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
+
+fn static_visibility<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ can_be_internalized: &mut bool,
+ def_id: DefId,
+) -> Visibility {
+ if tcx.is_reachable_non_generic(def_id) {
+ *can_be_internalized = false;
+ default_visibility(tcx, def_id, false)
+ } else {
+ Visibility::Hidden
+ }
+}
+
+fn mono_item_visibility<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mono_item: &MonoItem<'tcx>,
+ can_be_internalized: &mut bool,
+ export_generics: bool,
+) -> Visibility {
+ let instance = match mono_item {
+ // This is pretty complicated; see below.
+ MonoItem::Fn(instance) => instance,
+
+ // Misc handling for generics and such, but otherwise:
+ MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id),
+ MonoItem::GlobalAsm(item_id) => {
+ return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id());
+ }
+ };
+
+ let def_id = match instance.def {
+ InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id,
+
+ // We match the visibility of statics here
+ InstanceDef::ThreadLocalShim(def_id) => {
+ return static_visibility(tcx, can_be_internalized, def_id);
+ }
+
+ // These are all compiler glue and such, never exported, always hidden.
+ InstanceDef::VTableShim(..)
+ | InstanceDef::ReifyShim(..)
+ | InstanceDef::FnPtrShim(..)
+ | InstanceDef::Virtual(..)
+ | InstanceDef::Intrinsic(..)
+ | InstanceDef::ClosureOnceShim { .. }
+ | InstanceDef::DropGlue(..)
+ | InstanceDef::CloneShim(..)
+ | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
+ };
+
+ // The `start_fn` lang item is actually a monomorphized instance of a
+ // function in the standard library, used for the `main` function. We don't
+ // want to export it so we tag it with `Hidden` visibility but this symbol
+ // is only referenced from the actual `main` symbol which we unfortunately
+ // don't know anything about during partitioning/collection. As a result we
+ // forcibly keep this symbol out of the `internalization_candidates` set.
+ //
+ // FIXME: eventually we don't want to always force this symbol to have
+ // hidden visibility, it should indeed be a candidate for
+ // internalization, but we have to understand that it's referenced
+ // from the `main` symbol we'll generate later.
+ //
+ // This may be fixable with a new `InstanceDef` perhaps? Unsure!
+ if tcx.lang_items().start_fn() == Some(def_id) {
+ *can_be_internalized = false;
+ return Visibility::Hidden;
+ }
+
+ let is_generic = instance.substs.non_erasable_generics().next().is_some();
+
+ // Upstream `DefId` instances get different handling than local ones.
+ let Some(def_id) = def_id.as_local() else {
+ return if export_generics && is_generic {
+ // If it is an upstream monomorphization and we export generics, we must make
+ // it available to downstream crates.
+ *can_be_internalized = false;
+ default_visibility(tcx, def_id, true)
+ } else {
+ Visibility::Hidden
+ };
+ };
+
+ if is_generic {
+ if export_generics {
+ if tcx.is_unreachable_local_definition(def_id) {
+ // This instance cannot be used from another crate.
+ Visibility::Hidden
+ } else {
+ // This instance might be useful in a downstream crate.
+ *can_be_internalized = false;
+ default_visibility(tcx, def_id.to_def_id(), true)
+ }
+ } else {
+ // We are not exporting generics or the definition is not reachable
+ // for downstream crates, we can internalize its instantiations.
+ Visibility::Hidden
+ }
+ } else {
+ // If this isn't a generic function then we mark this a `Default` if
+ // this is a reachable item, meaning that it's a symbol other crates may
+ // use when they link to us.
+ if tcx.is_reachable_non_generic(def_id.to_def_id()) {
+ *can_be_internalized = false;
+ debug_assert!(!is_generic);
+ return default_visibility(tcx, def_id.to_def_id(), false);
+ }
+
+ // If this isn't reachable then we're gonna tag this with `Hidden`
+ // visibility. In some situations though we'll want to prevent this
+ // symbol from being internalized.
+ //
+ // There's two categories of items here:
+ //
+ // * First is weak lang items. These are basically mechanisms for
+ // libcore to forward-reference symbols defined later in crates like
+ // the standard library or `#[panic_handler]` definitions. The
+ // definition of these weak lang items needs to be referencable by
+ // libcore, so we're no longer a candidate for internalization.
+ // Removal of these functions can't be done by LLVM but rather must be
+ // done by the linker as it's a non-local decision.
+ //
+ // * Second is "std internal symbols". Currently this is primarily used
+ // for allocator symbols. Allocators are a little weird in their
+ // implementation, but the idea is that the compiler, at the last
+ // minute, defines an allocator with an injected object file. The
+ // `alloc` crate references these symbols (`__rust_alloc`) and the
+ // definition doesn't get hooked up until a linked crate artifact is
+ // generated.
+ //
+ // The symbols synthesized by the compiler (`__rust_alloc`) are thin
+ // veneers around the actual implementation, some other symbol which
+ // implements the same ABI. These symbols (things like `__rg_alloc`,
+ // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std
+ // internal symbols".
+ //
+ // The std-internal symbols here **should not show up in a dll as an
+ // exported interface**, so they return `false` from
+ // `is_reachable_non_generic` above and we'll give them `Hidden`
+ // visibility below. Like the weak lang items, though, we can't let
+ // LLVM internalize them as this decision is left up to the linker to
+ // omit them, so prevent them from being internalized.
+ let attrs = tcx.codegen_fn_attrs(def_id);
+ if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
+ *can_be_internalized = false;
+ }
+
+ Visibility::Hidden
+ }
+}
+
+fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
+ if !tcx.sess.target.default_hidden_visibility {
+ return Visibility::Default;
+ }
+
+ // Generic functions never have export-level C.
+ if is_generic {
+ return Visibility::Hidden;
+ }
+
+ // Things with export level C don't get instantiated in
+ // downstream crates.
+ if !id.is_local() {
+ return Visibility::Hidden;
+ }
+
+ // C-export level items remain at `Default`, all other internal
+ // items become `Hidden`.
+ match tcx.reachable_non_generics(id.krate).get(&id) {
+ Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default,
+ _ => Visibility::Hidden,
+ }
+}
+
+fn debug_dump<'a, 'tcx: 'a>(
+ tcx: TyCtxt<'tcx>,
+ label: &str,
+ cgus: &[CodegenUnit<'tcx>],
+ (unique_inlined_items, unique_inlined_size): (usize, usize),
+) {
+ let dump = move || {
+ use std::fmt::Write;
+
+ let mut num_cgus = 0;
+ let mut all_cgu_sizes = Vec::new();
+
+ // Note: every unique root item is placed exactly once, so the number
+ // of unique root items always equals the number of placed root items.
+
+ let mut root_items = 0;
+ // unique_inlined_items is passed in above.
+ let mut placed_inlined_items = 0;
+
+ let mut root_size = 0;
+ // unique_inlined_size is passed in above.
+ let mut placed_inlined_size = 0;
+
+ for cgu in cgus.iter() {
+ num_cgus += 1;
+ all_cgu_sizes.push(cgu.size_estimate());
+
+ for (item, _) in cgu.items() {
+ match item.instantiation_mode(tcx) {
+ InstantiationMode::GloballyShared { .. } => {
+ root_items += 1;
+ root_size += item.size_estimate(tcx);
+ }
+ InstantiationMode::LocalCopy => {
+ placed_inlined_items += 1;
+ placed_inlined_size += item.size_estimate(tcx);
+ }
+ }
+ }
+ }
+
+ all_cgu_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
+
+ let unique_items = root_items + unique_inlined_items;
+ let placed_items = root_items + placed_inlined_items;
+ let items_ratio = placed_items as f64 / unique_items as f64;
+
+ let unique_size = root_size + unique_inlined_size;
+ let placed_size = root_size + placed_inlined_size;
+ let size_ratio = placed_size as f64 / unique_size as f64;
+
+ let mean_cgu_size = placed_size as f64 / num_cgus as f64;
+
+ assert_eq!(placed_size, all_cgu_sizes.iter().sum::<usize>());
+
+ let s = &mut String::new();
+ let _ = writeln!(s, "{label}");
+ let _ = writeln!(
+ s,
+ "- unique items: {unique_items} ({root_items} root + {unique_inlined_items} inlined), \
+ unique size: {unique_size} ({root_size} root + {unique_inlined_size} inlined)\n\
+ - placed items: {placed_items} ({root_items} root + {placed_inlined_items} inlined), \
+ placed size: {placed_size} ({root_size} root + {placed_inlined_size} inlined)\n\
+ - placed/unique items ratio: {items_ratio:.2}, \
+ placed/unique size ratio: {size_ratio:.2}\n\
+ - CGUs: {num_cgus}, mean size: {mean_cgu_size:.1}, sizes: {}",
+ list(&all_cgu_sizes),
+ );
+ let _ = writeln!(s);
+
+ for (i, cgu) in cgus.iter().enumerate() {
+ let name = cgu.name();
+ let size = cgu.size_estimate();
+ let num_items = cgu.items().len();
+ let mean_size = size as f64 / num_items as f64;
+
+ let mut placed_item_sizes: Vec<_> =
+ cgu.items().iter().map(|(item, _)| item.size_estimate(tcx)).collect();
+ placed_item_sizes.sort_unstable_by_key(|&n| cmp::Reverse(n));
+ let sizes = list(&placed_item_sizes);
+
+ let _ = writeln!(s, "- CGU[{i}]");
+ let _ = writeln!(s, " - {name}, size: {size}");
+ let _ =
+ writeln!(s, " - items: {num_items}, mean size: {mean_size:.1}, sizes: {sizes}",);
+
+ for (item, linkage) in cgu.items_in_deterministic_order(tcx) {
+ let symbol_name = item.symbol_name(tcx).name;
+ let symbol_hash_start = symbol_name.rfind('h');
+ let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]);
+ let size = item.size_estimate(tcx);
+ let kind = match item.instantiation_mode(tcx) {
+ InstantiationMode::GloballyShared { .. } => "root",
+ InstantiationMode::LocalCopy => "inlined",
+ };
+ let _ = with_no_trimmed_paths!(writeln!(
+ s,
+ " - {item} [{linkage:?}] [{symbol_hash}] ({kind}, size: {size})"
+ ));
+ }
+
+ let _ = writeln!(s);
+ }
+
+ return std::mem::take(s);
+
+ // Converts a slice to a string, capturing repetitions to save space.
+ // E.g. `[4, 4, 4, 3, 2, 1, 1, 1, 1, 1]` -> "[4 (x3), 3, 2, 1 (x5)]".
+ fn list(ns: &[usize]) -> String {
+ let mut v = Vec::new();
+ if ns.is_empty() {
+ return "[]".to_string();
+ }
+
+ let mut elem = |curr, curr_count| {
+ if curr_count == 1 {
+ v.push(format!("{curr}"));
+ } else {
+ v.push(format!("{curr} (x{curr_count})"));
+ }
+ };
+
+ let mut curr = ns[0];
+ let mut curr_count = 1;
+
+ for &n in &ns[1..] {
+ if n != curr {
+ elem(curr, curr_count);
+ curr = n;
+ curr_count = 1;
+ } else {
+ curr_count += 1;
+ }
+ }
+ elem(curr, curr_count);
+
+ let mut s = "[".to_string();
+ s.push_str(&v.join(", "));
+ s.push_str("]");
+ s
+ }
+ };
+
+ debug!("{}", dump());
+}
+
+#[inline(never)] // give this a place in the profiler
+fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
+where
+ I: Iterator<Item = &'a MonoItem<'tcx>>,
+ 'tcx: 'a,
+{
+ let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
+
+ let mut symbols: Vec<_> =
+ mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
+
+ symbols.sort_by_key(|sym| sym.1);
+
+ for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
+ if sym1 == sym2 {
+ let span1 = mono_item1.local_span(tcx);
+ let span2 = mono_item2.local_span(tcx);
+
+ // Deterministically select one of the spans for error reporting
+ let span = match (span1, span2) {
+ (Some(span1), Some(span2)) => {
+ Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
+ }
+ (span1, span2) => span1.or(span2),
+ };
+
+ tcx.sess.emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
+ }
+ }
+}
+
+fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) {
+ let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items {
+ Some(ref s) => {
+ let mode = s.to_lowercase();
+ let mode = mode.trim();
+ if mode == "eager" {
+ MonoItemCollectionMode::Eager
+ } else {
+ if mode != "lazy" {
+ tcx.sess.emit_warning(UnknownCguCollectionMode { mode });
+ }
+
+ MonoItemCollectionMode::Lazy
+ }
+ }
+ None => {
+ if tcx.sess.link_dead_code() {
+ MonoItemCollectionMode::Eager
+ } else {
+ MonoItemCollectionMode::Lazy
+ }
+ }
+ };
+
+ let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode);
+
+ tcx.sess.abort_if_errors();
+
+ let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
+ sync::join(
+ || {
+ let mut codegen_units = partition(tcx, items.iter().copied(), &usage_map);
+ codegen_units[0].make_primary();
+ &*tcx.arena.alloc_from_iter(codegen_units)
+ },
+ || assert_symbols_are_distinct(tcx, items.iter()),
+ )
+ });
+
+ if tcx.prof.enabled() {
+ // Record CGU size estimates for self-profiling.
+ for cgu in codegen_units {
+ tcx.prof.artifact_size(
+ "codegen_unit_size_estimate",
+ cgu.name().as_str(),
+ cgu.size_estimate() as u64,
+ );
+ }
+ }
+
+ let mono_items: DefIdSet = items
+ .iter()
+ .filter_map(|mono_item| match *mono_item {
+ MonoItem::Fn(ref instance) => Some(instance.def_id()),
+ MonoItem::Static(def_id) => Some(def_id),
+ _ => None,
+ })
+ .collect();
+
+ // Output monomorphization stats per def_id
+ if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats {
+ if let Err(err) =
+ dump_mono_items_stats(tcx, &codegen_units, path, tcx.crate_name(LOCAL_CRATE))
+ {
+ tcx.sess.emit_fatal(CouldntDumpMonoStats { error: err.to_string() });
+ }
+ }
+
+ if tcx.sess.opts.unstable_opts.print_mono_items.is_some() {
+ let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
+
+ for cgu in codegen_units {
+ for (&mono_item, &linkage) in cgu.items() {
+ item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
+ }
+ }
+
+ let mut item_keys: Vec<_> = items
+ .iter()
+ .map(|i| {
+ let mut output = with_no_trimmed_paths!(i.to_string());
+ output.push_str(" @@");
+ let mut empty = Vec::new();
+ let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
+ cgus.sort_by_key(|(name, _)| *name);
+ cgus.dedup();
+ for &(ref cgu_name, (linkage, _)) in cgus.iter() {
+ output.push(' ');
+ output.push_str(cgu_name.as_str());
+
+ let linkage_abbrev = match linkage {
+ Linkage::External => "External",
+ Linkage::AvailableExternally => "Available",
+ Linkage::LinkOnceAny => "OnceAny",
+ Linkage::LinkOnceODR => "OnceODR",
+ Linkage::WeakAny => "WeakAny",
+ Linkage::WeakODR => "WeakODR",
+ Linkage::Appending => "Appending",
+ Linkage::Internal => "Internal",
+ Linkage::Private => "Private",
+ Linkage::ExternalWeak => "ExternalWeak",
+ Linkage::Common => "Common",
+ };
+
+ output.push('[');
+ output.push_str(linkage_abbrev);
+ output.push(']');
+ }
+ output
+ })
+ .collect();
+
+ item_keys.sort();
+
+ for item in item_keys {
+ println!("MONO_ITEM {item}");
+ }
+ }
+
+ (tcx.arena.alloc(mono_items), codegen_units)
+}
+
+/// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s
+/// def, to a file in the given output directory.
+fn dump_mono_items_stats<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ codegen_units: &[CodegenUnit<'tcx>],
+ output_directory: &Option<PathBuf>,
+ crate_name: Symbol,
+) -> Result<(), Box<dyn std::error::Error>> {
+ let output_directory = if let Some(ref directory) = output_directory {
+ fs::create_dir_all(directory)?;
+ directory
+ } else {
+ Path::new(".")
+ };
+
+ let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
+ let ext = format.extension();
+ let filename = format!("{crate_name}.mono_items.{ext}");
+ let output_path = output_directory.join(&filename);
+ let file = File::create(&output_path)?;
+ let mut file = BufWriter::new(file);
+
+ // Gather instantiated mono items grouped by def_id
+ let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default();
+ for cgu in codegen_units {
+ for (&mono_item, _) in cgu.items() {
+ // Avoid variable-sized compiler-generated shims
+ if mono_item.is_user_defined() {
+ items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
+ }
+ }
+ }
+
+ #[derive(serde::Serialize)]
+ struct MonoItem {
+ name: String,
+ instantiation_count: usize,
+ size_estimate: usize,
+ total_estimate: usize,
+ }
+
+ // Output stats sorted by total instantiated size, from heaviest to lightest
+ let mut stats: Vec<_> = items_per_def_id
+ .into_iter()
+ .map(|(def_id, items)| {
+ let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
+ let instantiation_count = items.len();
+ let size_estimate = items[0].size_estimate(tcx);
+ let total_estimate = instantiation_count * size_estimate;
+ MonoItem { name, instantiation_count, size_estimate, total_estimate }
+ })
+ .collect();
+ stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
+
+ if !stats.is_empty() {
+ match format {
+ DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
+ DumpMonoStatsFormat::Markdown => {
+ writeln!(
+ file,
+ "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
+ )?;
+ writeln!(file, "| --- | ---: | ---: | ---: |")?;
+
+ for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
+ writeln!(
+ file,
+ "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
+ )?;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
+ let (items, cgus) = tcx.collect_and_partition_mono_items(());
+ let mut visited = DefIdSet::default();
+ let mut result = items.clone();
+
+ for cgu in cgus {
+ for (item, _) in cgu.items() {
+ if let MonoItem::Fn(ref instance) = item {
+ let did = instance.def_id();
+ if !visited.insert(did) {
+ continue;
+ }
+ let body = tcx.instance_mir(instance.def);
+ for block in body.basic_blocks.iter() {
+ for statement in &block.statements {
+ let mir::StatementKind::Coverage(_) = statement.kind else { continue };
+ let scope = statement.source_info.scope;
+ if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
+ result.insert(inlined.def_id());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ tcx.arena.alloc(result)
+}
+
+pub fn provide(providers: &mut Providers) {
+ providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
+ providers.codegened_and_inlined_items = codegened_and_inlined_items;
+
+ providers.is_codegened_item = |tcx, def_id| {
+ let (all_mono_items, _) = tcx.collect_and_partition_mono_items(());
+ all_mono_items.contains(&def_id)
+ };
+
+ providers.codegen_unit = |tcx, name| {
+ let (_, all) = tcx.collect_and_partition_mono_items(());
+ all.iter()
+ .find(|cgu| cgu.name() == name)
+ .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}"))
+ };
+}
diff --git a/compiler/rustc_monomorphize/src/partitioning/default.rs b/compiler/rustc_monomorphize/src/partitioning/default.rs
deleted file mode 100644
index 603b3ddc1..000000000
--- a/compiler/rustc_monomorphize/src/partitioning/default.rs
+++ /dev/null
@@ -1,644 +0,0 @@
-use std::cmp;
-use std::collections::hash_map::Entry;
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_hir::definitions::DefPathDataName;
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
-use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility};
-use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
-use rustc_middle::ty::print::characteristic_def_id_of_type;
-use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceDef, TyCtxt};
-use rustc_span::symbol::Symbol;
-
-use super::PartitioningCx;
-use crate::collector::InliningMap;
-use crate::partitioning::{MonoItemPlacement, Partition, PlacedRootMonoItems};
-
-pub struct DefaultPartitioning;
-
-impl<'tcx> Partition<'tcx> for DefaultPartitioning {
- fn place_root_mono_items<I>(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- mono_items: &mut I,
- ) -> PlacedRootMonoItems<'tcx>
- where
- I: Iterator<Item = MonoItem<'tcx>>,
- {
- let mut roots = FxHashSet::default();
- let mut codegen_units = FxHashMap::default();
- let is_incremental_build = cx.tcx.sess.opts.incremental.is_some();
- let mut internalization_candidates = FxHashSet::default();
-
- // Determine if monomorphizations instantiated in this crate will be made
- // available to downstream crates. This depends on whether we are in
- // share-generics mode and whether the current crate can even have
- // downstream crates.
- let export_generics =
- cx.tcx.sess.opts.share_generics() && cx.tcx.local_crate_exports_generics();
-
- let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
- let cgu_name_cache = &mut FxHashMap::default();
-
- for mono_item in mono_items {
- match mono_item.instantiation_mode(cx.tcx) {
- InstantiationMode::GloballyShared { .. } => {}
- InstantiationMode::LocalCopy => continue,
- }
-
- let characteristic_def_id = characteristic_def_id_of_mono_item(cx.tcx, mono_item);
- let is_volatile = is_incremental_build && mono_item.is_generic_fn();
-
- let codegen_unit_name = match characteristic_def_id {
- Some(def_id) => compute_codegen_unit_name(
- cx.tcx,
- cgu_name_builder,
- def_id,
- is_volatile,
- cgu_name_cache,
- ),
- None => fallback_cgu_name(cgu_name_builder),
- };
-
- let codegen_unit = codegen_units
- .entry(codegen_unit_name)
- .or_insert_with(|| CodegenUnit::new(codegen_unit_name));
-
- let mut can_be_internalized = true;
- let (linkage, visibility) = mono_item_linkage_and_visibility(
- cx.tcx,
- &mono_item,
- &mut can_be_internalized,
- export_generics,
- );
- if visibility == Visibility::Hidden && can_be_internalized {
- internalization_candidates.insert(mono_item);
- }
-
- codegen_unit.items_mut().insert(mono_item, (linkage, visibility));
- roots.insert(mono_item);
- }
-
- // Always ensure we have at least one CGU; otherwise, if we have a
- // crate with just types (for example), we could wind up with no CGU.
- if codegen_units.is_empty() {
- let codegen_unit_name = fallback_cgu_name(cgu_name_builder);
- codegen_units.insert(codegen_unit_name, CodegenUnit::new(codegen_unit_name));
- }
-
- let codegen_units = codegen_units.into_values().collect();
- PlacedRootMonoItems { codegen_units, roots, internalization_candidates }
- }
-
- fn merge_codegen_units(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut Vec<CodegenUnit<'tcx>>,
- ) {
- assert!(cx.target_cgu_count >= 1);
-
- // Note that at this point in time the `codegen_units` here may not be
- // in a deterministic order (but we know they're deterministically the
- // same set). We want this merging to produce a deterministic ordering
- // of codegen units from the input.
- //
- // Due to basically how we've implemented the merging below (merge the
- // two smallest into each other) we're sure to start off with a
- // deterministic order (sorted by name). This'll mean that if two cgus
- // have the same size the stable sort below will keep everything nice
- // and deterministic.
- codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
-
- // This map keeps track of what got merged into what.
- let mut cgu_contents: FxHashMap<Symbol, Vec<Symbol>> =
- codegen_units.iter().map(|cgu| (cgu.name(), vec![cgu.name()])).collect();
-
- // Merge the two smallest codegen units until the target size is
- // reached.
- while codegen_units.len() > cx.target_cgu_count {
- // Sort small cgus to the back
- codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate()));
- let mut smallest = codegen_units.pop().unwrap();
- let second_smallest = codegen_units.last_mut().unwrap();
-
- // Move the mono-items from `smallest` to `second_smallest`
- second_smallest.modify_size_estimate(smallest.size_estimate());
- for (k, v) in smallest.items_mut().drain() {
- second_smallest.items_mut().insert(k, v);
- }
-
- // Record that `second_smallest` now contains all the stuff that was
- // in `smallest` before.
- let mut consumed_cgu_names = cgu_contents.remove(&smallest.name()).unwrap();
- cgu_contents.get_mut(&second_smallest.name()).unwrap().append(&mut consumed_cgu_names);
-
- debug!(
- "CodegenUnit {} merged into CodegenUnit {}",
- smallest.name(),
- second_smallest.name()
- );
- }
-
- let cgu_name_builder = &mut CodegenUnitNameBuilder::new(cx.tcx);
-
- if cx.tcx.sess.opts.incremental.is_some() {
- // If we are doing incremental compilation, we want CGU names to
- // reflect the path of the source level module they correspond to.
- // For CGUs that contain the code of multiple modules because of the
- // merging done above, we use a concatenation of the names of all
- // contained CGUs.
- let new_cgu_names: FxHashMap<Symbol, String> = cgu_contents
- .into_iter()
- // This `filter` makes sure we only update the name of CGUs that
- // were actually modified by merging.
- .filter(|(_, cgu_contents)| cgu_contents.len() > 1)
- .map(|(current_cgu_name, cgu_contents)| {
- let mut cgu_contents: Vec<&str> =
- cgu_contents.iter().map(|s| s.as_str()).collect();
-
- // Sort the names, so things are deterministic and easy to
- // predict. We are sorting primitive `&str`s here so we can
- // use unstable sort.
- cgu_contents.sort_unstable();
-
- (current_cgu_name, cgu_contents.join("--"))
- })
- .collect();
-
- for cgu in codegen_units.iter_mut() {
- if let Some(new_cgu_name) = new_cgu_names.get(&cgu.name()) {
- if cx.tcx.sess.opts.unstable_opts.human_readable_cgu_names {
- cgu.set_name(Symbol::intern(&new_cgu_name));
- } else {
- // If we don't require CGU names to be human-readable,
- // we use a fixed length hash of the composite CGU name
- // instead.
- let new_cgu_name = CodegenUnit::mangle_name(&new_cgu_name);
- cgu.set_name(Symbol::intern(&new_cgu_name));
- }
- }
- }
- } else {
- // If we are compiling non-incrementally we just generate simple CGU
- // names containing an index.
- for (index, cgu) in codegen_units.iter_mut().enumerate() {
- let numbered_codegen_unit_name =
- cgu_name_builder.build_cgu_name_no_mangle(LOCAL_CRATE, &["cgu"], Some(index));
- cgu.set_name(numbered_codegen_unit_name);
- }
- }
- }
-
- fn place_inlined_mono_items(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- roots: FxHashSet<MonoItem<'tcx>>,
- ) -> FxHashMap<MonoItem<'tcx>, MonoItemPlacement> {
- let mut mono_item_placements = FxHashMap::default();
-
- let single_codegen_unit = codegen_units.len() == 1;
-
- for old_codegen_unit in codegen_units.iter_mut() {
- // Collect all items that need to be available in this codegen unit.
- let mut reachable = FxHashSet::default();
- for root in old_codegen_unit.items().keys() {
- follow_inlining(*root, cx.inlining_map, &mut reachable);
- }
-
- let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name());
-
- // Add all monomorphizations that are not already there.
- for mono_item in reachable {
- if let Some(linkage) = old_codegen_unit.items().get(&mono_item) {
- // This is a root, just copy it over.
- new_codegen_unit.items_mut().insert(mono_item, *linkage);
- } else {
- if roots.contains(&mono_item) {
- bug!(
- "GloballyShared mono-item inlined into other CGU: \
- {:?}",
- mono_item
- );
- }
-
- // This is a CGU-private copy.
- new_codegen_unit
- .items_mut()
- .insert(mono_item, (Linkage::Internal, Visibility::Default));
- }
-
- if !single_codegen_unit {
- // If there is more than one codegen unit, we need to keep track
- // in which codegen units each monomorphization is placed.
- match mono_item_placements.entry(mono_item) {
- Entry::Occupied(e) => {
- let placement = e.into_mut();
- debug_assert!(match *placement {
- MonoItemPlacement::SingleCgu { cgu_name } => {
- cgu_name != new_codegen_unit.name()
- }
- MonoItemPlacement::MultipleCgus => true,
- });
- *placement = MonoItemPlacement::MultipleCgus;
- }
- Entry::Vacant(e) => {
- e.insert(MonoItemPlacement::SingleCgu {
- cgu_name: new_codegen_unit.name(),
- });
- }
- }
- }
- }
-
- *old_codegen_unit = new_codegen_unit;
- }
-
- return mono_item_placements;
-
- fn follow_inlining<'tcx>(
- mono_item: MonoItem<'tcx>,
- inlining_map: &InliningMap<'tcx>,
- visited: &mut FxHashSet<MonoItem<'tcx>>,
- ) {
- if !visited.insert(mono_item) {
- return;
- }
-
- inlining_map.with_inlining_candidates(mono_item, |target| {
- follow_inlining(target, inlining_map, visited);
- });
- }
- }
-
- fn internalize_symbols(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
- internalization_candidates: FxHashSet<MonoItem<'tcx>>,
- ) {
- if codegen_units.len() == 1 {
- // Fast path for when there is only one codegen unit. In this case we
- // can internalize all candidates, since there is nowhere else they
- // could be accessed from.
- for cgu in codegen_units {
- for candidate in &internalization_candidates {
- cgu.items_mut().insert(*candidate, (Linkage::Internal, Visibility::Default));
- }
- }
-
- return;
- }
-
- // Build a map from every monomorphization to all the monomorphizations that
- // reference it.
- let mut accessor_map: FxHashMap<MonoItem<'tcx>, Vec<MonoItem<'tcx>>> = Default::default();
- cx.inlining_map.iter_accesses(|accessor, accessees| {
- for accessee in accessees {
- accessor_map.entry(*accessee).or_default().push(accessor);
- }
- });
-
- // For each internalization candidates in each codegen unit, check if it is
- // accessed from outside its defining codegen unit.
- for cgu in codegen_units {
- let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name() };
-
- for (accessee, linkage_and_visibility) in cgu.items_mut() {
- if !internalization_candidates.contains(accessee) {
- // This item is no candidate for internalizing, so skip it.
- continue;
- }
- debug_assert_eq!(mono_item_placements[accessee], home_cgu);
-
- if let Some(accessors) = accessor_map.get(accessee) {
- if accessors
- .iter()
- .filter_map(|accessor| {
- // Some accessors might not have been
- // instantiated. We can safely ignore those.
- mono_item_placements.get(accessor)
- })
- .any(|placement| *placement != home_cgu)
- {
- // Found an accessor from another CGU, so skip to the next
- // item without marking this one as internal.
- continue;
- }
- }
-
- // If we got here, we did not find any accesses from other CGUs,
- // so it's fine to make this monomorphization internal.
- *linkage_and_visibility = (Linkage::Internal, Visibility::Default);
- }
- }
- }
-}
-
-fn characteristic_def_id_of_mono_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- mono_item: MonoItem<'tcx>,
-) -> Option<DefId> {
- match mono_item {
- MonoItem::Fn(instance) => {
- let def_id = match instance.def {
- ty::InstanceDef::Item(def) => def,
- ty::InstanceDef::VTableShim(..)
- | ty::InstanceDef::ReifyShim(..)
- | ty::InstanceDef::FnPtrShim(..)
- | ty::InstanceDef::ClosureOnceShim { .. }
- | ty::InstanceDef::Intrinsic(..)
- | ty::InstanceDef::DropGlue(..)
- | ty::InstanceDef::Virtual(..)
- | ty::InstanceDef::CloneShim(..)
- | ty::InstanceDef::ThreadLocalShim(..)
- | ty::InstanceDef::FnPtrAddrShim(..) => return None,
- };
-
- // If this is a method, we want to put it into the same module as
- // its self-type. If the self-type does not provide a characteristic
- // DefId, we use the location of the impl after all.
-
- if tcx.trait_of_item(def_id).is_some() {
- let self_ty = instance.substs.type_at(0);
- // This is a default implementation of a trait method.
- return characteristic_def_id_of_type(self_ty).or(Some(def_id));
- }
-
- if let Some(impl_def_id) = tcx.impl_of_method(def_id) {
- if tcx.sess.opts.incremental.is_some()
- && tcx.trait_id_of_impl(impl_def_id) == tcx.lang_items().drop_trait()
- {
- // Put `Drop::drop` into the same cgu as `drop_in_place`
- // since `drop_in_place` is the only thing that can
- // call it.
- return None;
- }
-
- // When polymorphization is enabled, methods which do not depend on their generic
- // parameters, but the self-type of their impl block do will fail to normalize.
- if !tcx.sess.opts.unstable_opts.polymorphize || !instance.has_param() {
- // This is a method within an impl, find out what the self-type is:
- let impl_self_ty = tcx.subst_and_normalize_erasing_regions(
- instance.substs,
- ty::ParamEnv::reveal_all(),
- tcx.type_of(impl_def_id),
- );
- if let Some(def_id) = characteristic_def_id_of_type(impl_self_ty) {
- return Some(def_id);
- }
- }
- }
-
- Some(def_id)
- }
- MonoItem::Static(def_id) => Some(def_id),
- MonoItem::GlobalAsm(item_id) => Some(item_id.owner_id.to_def_id()),
- }
-}
-
-fn compute_codegen_unit_name(
- tcx: TyCtxt<'_>,
- name_builder: &mut CodegenUnitNameBuilder<'_>,
- def_id: DefId,
- volatile: bool,
- cache: &mut CguNameCache,
-) -> Symbol {
- // Find the innermost module that is not nested within a function.
- let mut current_def_id = def_id;
- let mut cgu_def_id = None;
- // Walk backwards from the item we want to find the module for.
- loop {
- if current_def_id.is_crate_root() {
- if cgu_def_id.is_none() {
- // If we have not found a module yet, take the crate root.
- cgu_def_id = Some(def_id.krate.as_def_id());
- }
- break;
- } else if tcx.def_kind(current_def_id) == DefKind::Mod {
- if cgu_def_id.is_none() {
- cgu_def_id = Some(current_def_id);
- }
- } else {
- // If we encounter something that is not a module, throw away
- // any module that we've found so far because we now know that
- // it is nested within something else.
- cgu_def_id = None;
- }
-
- current_def_id = tcx.parent(current_def_id);
- }
-
- let cgu_def_id = cgu_def_id.unwrap();
-
- *cache.entry((cgu_def_id, volatile)).or_insert_with(|| {
- let def_path = tcx.def_path(cgu_def_id);
-
- let components = def_path.data.iter().map(|part| match part.data.name() {
- DefPathDataName::Named(name) => name,
- DefPathDataName::Anon { .. } => unreachable!(),
- });
-
- let volatile_suffix = volatile.then_some("volatile");
-
- name_builder.build_cgu_name(def_path.krate, components, volatile_suffix)
- })
-}
-
-// Anything we can't find a proper codegen unit for goes into this.
-fn fallback_cgu_name(name_builder: &mut CodegenUnitNameBuilder<'_>) -> Symbol {
- name_builder.build_cgu_name(LOCAL_CRATE, &["fallback"], Some("cgu"))
-}
-
-fn mono_item_linkage_and_visibility<'tcx>(
- tcx: TyCtxt<'tcx>,
- mono_item: &MonoItem<'tcx>,
- can_be_internalized: &mut bool,
- export_generics: bool,
-) -> (Linkage, Visibility) {
- if let Some(explicit_linkage) = mono_item.explicit_linkage(tcx) {
- return (explicit_linkage, Visibility::Default);
- }
- let vis = mono_item_visibility(tcx, mono_item, can_be_internalized, export_generics);
- (Linkage::External, vis)
-}
-
-type CguNameCache = FxHashMap<(DefId, bool), Symbol>;
-
-fn static_visibility<'tcx>(
- tcx: TyCtxt<'tcx>,
- can_be_internalized: &mut bool,
- def_id: DefId,
-) -> Visibility {
- if tcx.is_reachable_non_generic(def_id) {
- *can_be_internalized = false;
- default_visibility(tcx, def_id, false)
- } else {
- Visibility::Hidden
- }
-}
-
-fn mono_item_visibility<'tcx>(
- tcx: TyCtxt<'tcx>,
- mono_item: &MonoItem<'tcx>,
- can_be_internalized: &mut bool,
- export_generics: bool,
-) -> Visibility {
- let instance = match mono_item {
- // This is pretty complicated; see below.
- MonoItem::Fn(instance) => instance,
-
- // Misc handling for generics and such, but otherwise:
- MonoItem::Static(def_id) => return static_visibility(tcx, can_be_internalized, *def_id),
- MonoItem::GlobalAsm(item_id) => {
- return static_visibility(tcx, can_be_internalized, item_id.owner_id.to_def_id());
- }
- };
-
- let def_id = match instance.def {
- InstanceDef::Item(def_id) | InstanceDef::DropGlue(def_id, Some(_)) => def_id,
-
- // We match the visibility of statics here
- InstanceDef::ThreadLocalShim(def_id) => {
- return static_visibility(tcx, can_be_internalized, def_id);
- }
-
- // These are all compiler glue and such, never exported, always hidden.
- InstanceDef::VTableShim(..)
- | InstanceDef::ReifyShim(..)
- | InstanceDef::FnPtrShim(..)
- | InstanceDef::Virtual(..)
- | InstanceDef::Intrinsic(..)
- | InstanceDef::ClosureOnceShim { .. }
- | InstanceDef::DropGlue(..)
- | InstanceDef::CloneShim(..)
- | InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,
- };
-
- // The `start_fn` lang item is actually a monomorphized instance of a
- // function in the standard library, used for the `main` function. We don't
- // want to export it so we tag it with `Hidden` visibility but this symbol
- // is only referenced from the actual `main` symbol which we unfortunately
- // don't know anything about during partitioning/collection. As a result we
- // forcibly keep this symbol out of the `internalization_candidates` set.
- //
- // FIXME: eventually we don't want to always force this symbol to have
- // hidden visibility, it should indeed be a candidate for
- // internalization, but we have to understand that it's referenced
- // from the `main` symbol we'll generate later.
- //
- // This may be fixable with a new `InstanceDef` perhaps? Unsure!
- if tcx.lang_items().start_fn() == Some(def_id) {
- *can_be_internalized = false;
- return Visibility::Hidden;
- }
-
- let is_generic = instance.substs.non_erasable_generics().next().is_some();
-
- // Upstream `DefId` instances get different handling than local ones.
- let Some(def_id) = def_id.as_local() else {
- return if export_generics && is_generic {
- // If it is an upstream monomorphization and we export generics, we must make
- // it available to downstream crates.
- *can_be_internalized = false;
- default_visibility(tcx, def_id, true)
- } else {
- Visibility::Hidden
- };
- };
-
- if is_generic {
- if export_generics {
- if tcx.is_unreachable_local_definition(def_id) {
- // This instance cannot be used from another crate.
- Visibility::Hidden
- } else {
- // This instance might be useful in a downstream crate.
- *can_be_internalized = false;
- default_visibility(tcx, def_id.to_def_id(), true)
- }
- } else {
- // We are not exporting generics or the definition is not reachable
- // for downstream crates, we can internalize its instantiations.
- Visibility::Hidden
- }
- } else {
- // If this isn't a generic function then we mark this a `Default` if
- // this is a reachable item, meaning that it's a symbol other crates may
- // access when they link to us.
- if tcx.is_reachable_non_generic(def_id.to_def_id()) {
- *can_be_internalized = false;
- debug_assert!(!is_generic);
- return default_visibility(tcx, def_id.to_def_id(), false);
- }
-
- // If this isn't reachable then we're gonna tag this with `Hidden`
- // visibility. In some situations though we'll want to prevent this
- // symbol from being internalized.
- //
- // There's two categories of items here:
- //
- // * First is weak lang items. These are basically mechanisms for
- // libcore to forward-reference symbols defined later in crates like
- // the standard library or `#[panic_handler]` definitions. The
- // definition of these weak lang items needs to be referencable by
- // libcore, so we're no longer a candidate for internalization.
- // Removal of these functions can't be done by LLVM but rather must be
- // done by the linker as it's a non-local decision.
- //
- // * Second is "std internal symbols". Currently this is primarily used
- // for allocator symbols. Allocators are a little weird in their
- // implementation, but the idea is that the compiler, at the last
- // minute, defines an allocator with an injected object file. The
- // `alloc` crate references these symbols (`__rust_alloc`) and the
- // definition doesn't get hooked up until a linked crate artifact is
- // generated.
- //
- // The symbols synthesized by the compiler (`__rust_alloc`) are thin
- // veneers around the actual implementation, some other symbol which
- // implements the same ABI. These symbols (things like `__rg_alloc`,
- // `__rdl_alloc`, `__rde_alloc`, etc), are all tagged with "std
- // internal symbols".
- //
- // The std-internal symbols here **should not show up in a dll as an
- // exported interface**, so they return `false` from
- // `is_reachable_non_generic` above and we'll give them `Hidden`
- // visibility below. Like the weak lang items, though, we can't let
- // LLVM internalize them as this decision is left up to the linker to
- // omit them, so prevent them from being internalized.
- let attrs = tcx.codegen_fn_attrs(def_id);
- if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
- *can_be_internalized = false;
- }
-
- Visibility::Hidden
- }
-}
-
-fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility {
- if !tcx.sess.target.default_hidden_visibility {
- return Visibility::Default;
- }
-
- // Generic functions never have export-level C.
- if is_generic {
- return Visibility::Hidden;
- }
-
- // Things with export level C don't get instantiated in
- // downstream crates.
- if !id.is_local() {
- return Visibility::Hidden;
- }
-
- // C-export level items remain at `Default`, all other internal
- // items become `Hidden`.
- match tcx.reachable_non_generics(id.krate).get(&id) {
- Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default,
- _ => Visibility::Hidden,
- }
-}
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
deleted file mode 100644
index d0b23ca9e..000000000
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ /dev/null
@@ -1,673 +0,0 @@
-//! Partitioning Codegen Units for Incremental Compilation
-//! ======================================================
-//!
-//! The task of this module is to take the complete set of monomorphizations of
-//! a crate and produce a set of codegen units from it, where a codegen unit
-//! is a named set of (mono-item, linkage) pairs. That is, this module
-//! decides which monomorphization appears in which codegen units with which
-//! linkage. The following paragraphs describe some of the background on the
-//! partitioning scheme.
-//!
-//! The most important opportunity for saving on compilation time with
-//! incremental compilation is to avoid re-codegenning and re-optimizing code.
-//! Since the unit of codegen and optimization for LLVM is "modules" or, how
-//! we call them "codegen units", the particulars of how much time can be saved
-//! by incremental compilation are tightly linked to how the output program is
-//! partitioned into these codegen units prior to passing it to LLVM --
-//! especially because we have to treat codegen units as opaque entities once
-//! they are created: There is no way for us to incrementally update an existing
-//! LLVM module and so we have to build any such module from scratch if it was
-//! affected by some change in the source code.
-//!
-//! From that point of view it would make sense to maximize the number of
-//! codegen units by, for example, putting each function into its own module.
-//! That way only those modules would have to be re-compiled that were actually
-//! affected by some change, minimizing the number of functions that could have
-//! been re-used but just happened to be located in a module that is
-//! re-compiled.
-//!
-//! However, since LLVM optimization does not work across module boundaries,
-//! using such a highly granular partitioning would lead to very slow runtime
-//! code since it would effectively prohibit inlining and other inter-procedure
-//! optimizations. We want to avoid that as much as possible.
-//!
-//! Thus we end up with a trade-off: The bigger the codegen units, the better
-//! LLVM's optimizer can do its work, but also the smaller the compilation time
-//! reduction we get from incremental compilation.
-//!
-//! Ideally, we would create a partitioning such that there are few big codegen
-//! units with few interdependencies between them. For now though, we use the
-//! following heuristic to determine the partitioning:
-//!
-//! - There are two codegen units for every source-level module:
-//! - One for "stable", that is non-generic, code
-//! - One for more "volatile" code, i.e., monomorphized instances of functions
-//! defined in that module
-//!
-//! In order to see why this heuristic makes sense, let's take a look at when a
-//! codegen unit can get invalidated:
-//!
-//! 1. The most straightforward case is when the BODY of a function or global
-//! changes. Then any codegen unit containing the code for that item has to be
-//! re-compiled. Note that this includes all codegen units where the function
-//! has been inlined.
-//!
-//! 2. The next case is when the SIGNATURE of a function or global changes. In
-//! this case, all codegen units containing a REFERENCE to that item have to be
-//! re-compiled. This is a superset of case 1.
-//!
-//! 3. The final and most subtle case is when a REFERENCE to a generic function
-//! is added or removed somewhere. Even though the definition of the function
-//! might be unchanged, a new REFERENCE might introduce a new monomorphized
-//! instance of this function which has to be placed and compiled somewhere.
-//! Conversely, when removing a REFERENCE, it might have been the last one with
-//! that particular set of generic arguments and thus we have to remove it.
-//!
-//! From the above we see that just using one codegen unit per source-level
-//! module is not such a good idea, since just adding a REFERENCE to some
-//! generic item somewhere else would invalidate everything within the module
-//! containing the generic item. The heuristic above reduces this detrimental
-//! side-effect of references a little by at least not touching the non-generic
-//! code of the module.
-//!
-//! A Note on Inlining
-//! ------------------
-//! As briefly mentioned above, in order for LLVM to be able to inline a
-//! function call, the body of the function has to be available in the LLVM
-//! module where the call is made. This has a few consequences for partitioning:
-//!
-//! - The partitioning algorithm has to take care of placing functions into all
-//! codegen units where they should be available for inlining. It also has to
-//! decide on the correct linkage for these functions.
-//!
-//! - The partitioning algorithm has to know which functions are likely to get
-//! inlined, so it can distribute function instantiations accordingly. Since
-//! there is no way of knowing for sure which functions LLVM will decide to
-//! inline in the end, we apply a heuristic here: Only functions marked with
-//! `#[inline]` are considered for inlining by the partitioner. The current
-//! implementation will not try to determine if a function is likely to be
-//! inlined by looking at the functions definition.
-//!
-//! Note though that as a side-effect of creating a codegen units per
-//! source-level module, functions from the same module will be available for
-//! inlining, even when they are not marked `#[inline]`.
-
-mod default;
-
-use std::cmp;
-use std::fs::{self, File};
-use std::io::{BufWriter, Write};
-use std::path::{Path, PathBuf};
-
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync;
-use rustc_hir::def_id::{DefIdSet, LOCAL_CRATE};
-use rustc_middle::mir;
-use rustc_middle::mir::mono::MonoItem;
-use rustc_middle::mir::mono::{CodegenUnit, Linkage};
-use rustc_middle::query::Providers;
-use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath};
-use rustc_span::symbol::Symbol;
-
-use crate::collector::InliningMap;
-use crate::collector::{self, MonoItemCollectionMode};
-use crate::errors::{
- CouldntDumpMonoStats, SymbolAlreadyDefined, UnknownCguCollectionMode, UnknownPartitionStrategy,
-};
-
-enum Partitioner {
- Default(default::DefaultPartitioning),
- // Other partitioning strategies can go here.
- Unknown,
-}
-
-impl<'tcx> Partition<'tcx> for Partitioner {
- fn place_root_mono_items<I>(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- mono_items: &mut I,
- ) -> PlacedRootMonoItems<'tcx>
- where
- I: Iterator<Item = MonoItem<'tcx>>,
- {
- match self {
- Partitioner::Default(partitioner) => partitioner.place_root_mono_items(cx, mono_items),
- Partitioner::Unknown => cx.tcx.sess.emit_fatal(UnknownPartitionStrategy),
- }
- }
-
- fn merge_codegen_units(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut Vec<CodegenUnit<'tcx>>,
- ) {
- match self {
- Partitioner::Default(partitioner) => partitioner.merge_codegen_units(cx, codegen_units),
- Partitioner::Unknown => cx.tcx.sess.emit_fatal(UnknownPartitionStrategy),
- }
- }
-
- fn place_inlined_mono_items(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- roots: FxHashSet<MonoItem<'tcx>>,
- ) -> FxHashMap<MonoItem<'tcx>, MonoItemPlacement> {
- match self {
- Partitioner::Default(partitioner) => {
- partitioner.place_inlined_mono_items(cx, codegen_units, roots)
- }
- Partitioner::Unknown => cx.tcx.sess.emit_fatal(UnknownPartitionStrategy),
- }
- }
-
- fn internalize_symbols(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
- internalization_candidates: FxHashSet<MonoItem<'tcx>>,
- ) {
- match self {
- Partitioner::Default(partitioner) => partitioner.internalize_symbols(
- cx,
- codegen_units,
- mono_item_placements,
- internalization_candidates,
- ),
- Partitioner::Unknown => cx.tcx.sess.emit_fatal(UnknownPartitionStrategy),
- }
- }
-}
-
-struct PartitioningCx<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- target_cgu_count: usize,
- inlining_map: &'a InliningMap<'tcx>,
-}
-
-pub struct PlacedRootMonoItems<'tcx> {
- codegen_units: Vec<CodegenUnit<'tcx>>,
- roots: FxHashSet<MonoItem<'tcx>>,
- internalization_candidates: FxHashSet<MonoItem<'tcx>>,
-}
-
-trait Partition<'tcx> {
- fn place_root_mono_items<I>(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- mono_items: &mut I,
- ) -> PlacedRootMonoItems<'tcx>
- where
- I: Iterator<Item = MonoItem<'tcx>>;
-
- fn merge_codegen_units(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut Vec<CodegenUnit<'tcx>>,
- );
-
- fn place_inlined_mono_items(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- roots: FxHashSet<MonoItem<'tcx>>,
- ) -> FxHashMap<MonoItem<'tcx>, MonoItemPlacement>;
-
- fn internalize_symbols(
- &mut self,
- cx: &PartitioningCx<'_, 'tcx>,
- codegen_units: &mut [CodegenUnit<'tcx>],
- mono_item_placements: FxHashMap<MonoItem<'tcx>, MonoItemPlacement>,
- internalization_candidates: FxHashSet<MonoItem<'tcx>>,
- );
-}
-
-fn get_partitioner(tcx: TyCtxt<'_>) -> Partitioner {
- let strategy = match &tcx.sess.opts.unstable_opts.cgu_partitioning_strategy {
- None => "default",
- Some(s) => &s[..],
- };
-
- match strategy {
- "default" => Partitioner::Default(default::DefaultPartitioning),
- _ => Partitioner::Unknown,
- }
-}
-
-fn partition<'tcx, I>(
- tcx: TyCtxt<'tcx>,
- mono_items: &mut I,
- max_cgu_count: usize,
- inlining_map: &InliningMap<'tcx>,
-) -> Vec<CodegenUnit<'tcx>>
-where
- I: Iterator<Item = MonoItem<'tcx>>,
-{
- let _prof_timer = tcx.prof.generic_activity("cgu_partitioning");
-
- let mut partitioner = get_partitioner(tcx);
- let cx = &PartitioningCx { tcx, target_cgu_count: max_cgu_count, inlining_map };
- // In the first step, we place all regular monomorphizations into their
- // respective 'home' codegen unit. Regular monomorphizations are all
- // functions and statics defined in the local crate.
- let PlacedRootMonoItems { mut codegen_units, roots, internalization_candidates } = {
- let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_roots");
- partitioner.place_root_mono_items(cx, mono_items)
- };
-
- for cgu in &mut codegen_units {
- cgu.create_size_estimate(tcx);
- }
-
- debug_dump(tcx, "INITIAL PARTITIONING", &codegen_units);
-
- // Merge until we have at most `max_cgu_count` codegen units.
- // `merge_codegen_units` is responsible for updating the CGU size
- // estimates.
- {
- let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_merge_cgus");
- partitioner.merge_codegen_units(cx, &mut codegen_units);
- debug_dump(tcx, "POST MERGING", &codegen_units);
- }
-
- // In the next step, we use the inlining map to determine which additional
- // monomorphizations have to go into each codegen unit. These additional
- // monomorphizations can be drop-glue, functions from external crates, and
- // local functions the definition of which is marked with `#[inline]`.
- let mono_item_placements = {
- let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_place_inline_items");
- partitioner.place_inlined_mono_items(cx, &mut codegen_units, roots)
- };
-
- for cgu in &mut codegen_units {
- cgu.create_size_estimate(tcx);
- }
-
- debug_dump(tcx, "POST INLINING", &codegen_units);
-
- // Next we try to make as many symbols "internal" as possible, so LLVM has
- // more freedom to optimize.
- if !tcx.sess.link_dead_code() {
- let _prof_timer = tcx.prof.generic_activity("cgu_partitioning_internalize_symbols");
- partitioner.internalize_symbols(
- cx,
- &mut codegen_units,
- mono_item_placements,
- internalization_candidates,
- );
- }
-
- let instrument_dead_code =
- tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions();
-
- if instrument_dead_code {
- assert!(
- codegen_units.len() > 0,
- "There must be at least one CGU that code coverage data can be generated in."
- );
-
- // Find the smallest CGU that has exported symbols and put the dead
- // function stubs in that CGU. We look for exported symbols to increase
- // the likelihood the linker won't throw away the dead functions.
- // FIXME(#92165): In order to truly resolve this, we need to make sure
- // the object file (CGU) containing the dead function stubs is included
- // in the final binary. This will probably require forcing these
- // function symbols to be included via `-u` or `/include` linker args.
- let mut cgus: Vec<_> = codegen_units.iter_mut().collect();
- cgus.sort_by_key(|cgu| cgu.size_estimate());
-
- let dead_code_cgu =
- if let Some(cgu) = cgus.into_iter().rev().find(|cgu| {
- cgu.items().iter().any(|(_, (linkage, _))| *linkage == Linkage::External)
- }) {
- cgu
- } else {
- // If there are no CGUs that have externally linked items,
- // then we just pick the first CGU as a fallback.
- &mut codegen_units[0]
- };
- dead_code_cgu.make_code_coverage_dead_code_cgu();
- }
-
- // Finally, sort by codegen unit name, so that we get deterministic results.
- codegen_units.sort_by(|a, b| a.name().as_str().cmp(b.name().as_str()));
-
- debug_dump(tcx, "FINAL", &codegen_units);
-
- codegen_units
-}
-
-/// For symbol internalization, we need to know whether a symbol/mono-item is
-/// accessed from outside the codegen unit it is defined in. This type is used
-/// to keep track of that.
-#[derive(Clone, PartialEq, Eq, Debug)]
-enum MonoItemPlacement {
- SingleCgu { cgu_name: Symbol },
- MultipleCgus,
-}
-
-fn debug_dump<'a, 'tcx: 'a>(tcx: TyCtxt<'tcx>, label: &str, cgus: &[CodegenUnit<'tcx>]) {
- let dump = move || {
- use std::fmt::Write;
-
- let num_cgus = cgus.len();
- let max = cgus.iter().map(|cgu| cgu.size_estimate()).max().unwrap();
- let min = cgus.iter().map(|cgu| cgu.size_estimate()).min().unwrap();
- let ratio = max as f64 / min as f64;
-
- let s = &mut String::new();
- let _ = writeln!(
- s,
- "{label} ({num_cgus} CodegenUnits, max={max}, min={min}, max/min={ratio:.1}):"
- );
- for cgu in cgus {
- let _ =
- writeln!(s, "CodegenUnit {} estimated size {}:", cgu.name(), cgu.size_estimate());
-
- for (mono_item, linkage) in cgu.items() {
- let symbol_name = mono_item.symbol_name(tcx).name;
- let symbol_hash_start = symbol_name.rfind('h');
- let symbol_hash = symbol_hash_start.map_or("<no hash>", |i| &symbol_name[i..]);
-
- let _ = with_no_trimmed_paths!(writeln!(
- s,
- " - {} [{:?}] [{}] estimated size {}",
- mono_item,
- linkage,
- symbol_hash,
- mono_item.size_estimate(tcx)
- ));
- }
-
- let _ = writeln!(s);
- }
-
- std::mem::take(s)
- };
-
- debug!("{}", dump());
-}
-
-#[inline(never)] // give this a place in the profiler
-fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'tcx>, mono_items: I)
-where
- I: Iterator<Item = &'a MonoItem<'tcx>>,
- 'tcx: 'a,
-{
- let _prof_timer = tcx.prof.generic_activity("assert_symbols_are_distinct");
-
- let mut symbols: Vec<_> =
- mono_items.map(|mono_item| (mono_item, mono_item.symbol_name(tcx))).collect();
-
- symbols.sort_by_key(|sym| sym.1);
-
- for &[(mono_item1, ref sym1), (mono_item2, ref sym2)] in symbols.array_windows() {
- if sym1 == sym2 {
- let span1 = mono_item1.local_span(tcx);
- let span2 = mono_item2.local_span(tcx);
-
- // Deterministically select one of the spans for error reporting
- let span = match (span1, span2) {
- (Some(span1), Some(span2)) => {
- Some(if span1.lo().0 > span2.lo().0 { span1 } else { span2 })
- }
- (span1, span2) => span1.or(span2),
- };
-
- tcx.sess.emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
- }
- }
-}
-
-fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) {
- let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items {
- Some(ref s) => {
- let mode = s.to_lowercase();
- let mode = mode.trim();
- if mode == "eager" {
- MonoItemCollectionMode::Eager
- } else {
- if mode != "lazy" {
- tcx.sess.emit_warning(UnknownCguCollectionMode { mode });
- }
-
- MonoItemCollectionMode::Lazy
- }
- }
- None => {
- if tcx.sess.link_dead_code() {
- MonoItemCollectionMode::Eager
- } else {
- MonoItemCollectionMode::Lazy
- }
- }
- };
-
- let (items, inlining_map) = collector::collect_crate_mono_items(tcx, collection_mode);
-
- tcx.sess.abort_if_errors();
-
- let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || {
- sync::join(
- || {
- let mut codegen_units = partition(
- tcx,
- &mut items.iter().copied(),
- tcx.sess.codegen_units(),
- &inlining_map,
- );
- codegen_units[0].make_primary();
- &*tcx.arena.alloc_from_iter(codegen_units)
- },
- || assert_symbols_are_distinct(tcx, items.iter()),
- )
- });
-
- if tcx.prof.enabled() {
- // Record CGU size estimates for self-profiling.
- for cgu in codegen_units {
- tcx.prof.artifact_size(
- "codegen_unit_size_estimate",
- cgu.name().as_str(),
- cgu.size_estimate() as u64,
- );
- }
- }
-
- let mono_items: DefIdSet = items
- .iter()
- .filter_map(|mono_item| match *mono_item {
- MonoItem::Fn(ref instance) => Some(instance.def_id()),
- MonoItem::Static(def_id) => Some(def_id),
- _ => None,
- })
- .collect();
-
- // Output monomorphization stats per def_id
- if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats {
- if let Err(err) =
- dump_mono_items_stats(tcx, &codegen_units, path, tcx.crate_name(LOCAL_CRATE))
- {
- tcx.sess.emit_fatal(CouldntDumpMonoStats { error: err.to_string() });
- }
- }
-
- if tcx.sess.opts.unstable_opts.print_mono_items.is_some() {
- let mut item_to_cgus: FxHashMap<_, Vec<_>> = Default::default();
-
- for cgu in codegen_units {
- for (&mono_item, &linkage) in cgu.items() {
- item_to_cgus.entry(mono_item).or_default().push((cgu.name(), linkage));
- }
- }
-
- let mut item_keys: Vec<_> = items
- .iter()
- .map(|i| {
- let mut output = with_no_trimmed_paths!(i.to_string());
- output.push_str(" @@");
- let mut empty = Vec::new();
- let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty);
- cgus.sort_by_key(|(name, _)| *name);
- cgus.dedup();
- for &(ref cgu_name, (linkage, _)) in cgus.iter() {
- output.push(' ');
- output.push_str(cgu_name.as_str());
-
- let linkage_abbrev = match linkage {
- Linkage::External => "External",
- Linkage::AvailableExternally => "Available",
- Linkage::LinkOnceAny => "OnceAny",
- Linkage::LinkOnceODR => "OnceODR",
- Linkage::WeakAny => "WeakAny",
- Linkage::WeakODR => "WeakODR",
- Linkage::Appending => "Appending",
- Linkage::Internal => "Internal",
- Linkage::Private => "Private",
- Linkage::ExternalWeak => "ExternalWeak",
- Linkage::Common => "Common",
- };
-
- output.push('[');
- output.push_str(linkage_abbrev);
- output.push(']');
- }
- output
- })
- .collect();
-
- item_keys.sort();
-
- for item in item_keys {
- println!("MONO_ITEM {item}");
- }
- }
-
- (tcx.arena.alloc(mono_items), codegen_units)
-}
-
-/// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s
-/// def, to a file in the given output directory.
-fn dump_mono_items_stats<'tcx>(
- tcx: TyCtxt<'tcx>,
- codegen_units: &[CodegenUnit<'tcx>],
- output_directory: &Option<PathBuf>,
- crate_name: Symbol,
-) -> Result<(), Box<dyn std::error::Error>> {
- let output_directory = if let Some(ref directory) = output_directory {
- fs::create_dir_all(directory)?;
- directory
- } else {
- Path::new(".")
- };
-
- let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format;
- let ext = format.extension();
- let filename = format!("{crate_name}.mono_items.{ext}");
- let output_path = output_directory.join(&filename);
- let file = File::create(&output_path)?;
- let mut file = BufWriter::new(file);
-
- // Gather instantiated mono items grouped by def_id
- let mut items_per_def_id: FxHashMap<_, Vec<_>> = Default::default();
- for cgu in codegen_units {
- for (&mono_item, _) in cgu.items() {
- // Avoid variable-sized compiler-generated shims
- if mono_item.is_user_defined() {
- items_per_def_id.entry(mono_item.def_id()).or_default().push(mono_item);
- }
- }
- }
-
- #[derive(serde::Serialize)]
- struct MonoItem {
- name: String,
- instantiation_count: usize,
- size_estimate: usize,
- total_estimate: usize,
- }
-
- // Output stats sorted by total instantiated size, from heaviest to lightest
- let mut stats: Vec<_> = items_per_def_id
- .into_iter()
- .map(|(def_id, items)| {
- let name = with_no_trimmed_paths!(tcx.def_path_str(def_id));
- let instantiation_count = items.len();
- let size_estimate = items[0].size_estimate(tcx);
- let total_estimate = instantiation_count * size_estimate;
- MonoItem { name, instantiation_count, size_estimate, total_estimate }
- })
- .collect();
- stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate));
-
- if !stats.is_empty() {
- match format {
- DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?,
- DumpMonoStatsFormat::Markdown => {
- writeln!(
- file,
- "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |"
- )?;
- writeln!(file, "| --- | ---: | ---: | ---: |")?;
-
- for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats {
- writeln!(
- file,
- "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |"
- )?;
- }
- }
- }
- }
-
- Ok(())
-}
-
-fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
- let (items, cgus) = tcx.collect_and_partition_mono_items(());
- let mut visited = DefIdSet::default();
- let mut result = items.clone();
-
- for cgu in cgus {
- for (item, _) in cgu.items() {
- if let MonoItem::Fn(ref instance) = item {
- let did = instance.def_id();
- if !visited.insert(did) {
- continue;
- }
- let body = tcx.instance_mir(instance.def);
- for block in body.basic_blocks.iter() {
- for statement in &block.statements {
- let mir::StatementKind::Coverage(_) = statement.kind else { continue };
- let scope = statement.source_info.scope;
- if let Some(inlined) = scope.inlined_instance(&body.source_scopes) {
- result.insert(inlined.def_id());
- }
- }
- }
- }
- }
- }
-
- tcx.arena.alloc(result)
-}
-
-pub fn provide(providers: &mut Providers) {
- providers.collect_and_partition_mono_items = collect_and_partition_mono_items;
- providers.codegened_and_inlined_items = codegened_and_inlined_items;
-
- providers.is_codegened_item = |tcx, def_id| {
- let (all_mono_items, _) = tcx.collect_and_partition_mono_items(());
- all_mono_items.contains(&def_id)
- };
-
- providers.codegen_unit = |tcx, name| {
- let (_, all) = tcx.collect_and_partition_mono_items(());
- all.iter()
- .find(|cgu| cgu.name() == name)
- .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}"))
- };
-}
diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs
index d12bfc6f6..f6a80b043 100644
--- a/compiler/rustc_monomorphize/src/util.rs
+++ b/compiler/rustc_monomorphize/src/util.rs
@@ -29,12 +29,12 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In
let before_feature_tys = tcx.subst_and_normalize_erasing_regions(
closure_instance.substs,
param_env,
- ty::EarlyBinder(before_feature_tys),
+ ty::EarlyBinder::bind(before_feature_tys),
);
let after_feature_tys = tcx.subst_and_normalize_erasing_regions(
closure_instance.substs,
param_env,
- ty::EarlyBinder(after_feature_tys),
+ ty::EarlyBinder::bind(after_feature_tys),
);
let new_size = tcx
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 926339450..9787d98c1 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -353,6 +353,7 @@ parse_int_literal_too_large = integer literal is too large
parse_invalid_block_macro_segment = cannot use a `block` macro fragment here
.label = the `block` fragment is within this context
+ .suggestion = wrap this in another block
parse_invalid_char_in_escape = {parse_invalid_char_in_escape_msg}: `{$ch}`
.label = {parse_invalid_char_in_escape_msg}
@@ -695,7 +696,7 @@ parse_struct_literal_body_without_path =
parse_struct_literal_needing_parens =
invalid struct literal
- .suggestion = you might need to surround the struct literal in parentheses
+ .suggestion = you might need to surround the struct literal with parentheses
parse_struct_literal_not_allowed_here = struct literals are not allowed here
.suggestion = surround the struct literal with parentheses
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 84494eab8..96e1c0e3c 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -333,6 +333,17 @@ pub(crate) struct InvalidBlockMacroSegment {
pub span: Span,
#[label]
pub context: Span,
+ #[subdiagnostic]
+ pub wrap: WrapInExplicitBlock,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct WrapInExplicitBlock {
+ #[suggestion_part(code = "{{ ")]
+ pub lo: Span,
+ #[suggestion_part(code = " }}")]
+ pub hi: Span,
}
#[derive(Diagnostic)]
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index eb9625f92..461a34b67 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -158,7 +158,7 @@ pub(crate) fn emit_unescape_error(
diag.help(
"for more information, visit \
- <https://static.rust-lang.org/doc/master/reference.html#literals>",
+ <https://doc.rust-lang.org/reference/tokens.html#literals>",
);
}
diag.emit();
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index e1db19557..ee0abba1c 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -422,15 +422,12 @@ impl<'a> Parser<'a> {
}
}
-pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool {
- // One of the attributes may either itself be a macro,
- // or expand to macro attributes (`cfg_attr`).
- attrs.iter().any(|attr| {
- if attr.is_doc_comment() {
- return false;
- }
- attr.ident().map_or(true, |ident| {
- ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name)
- })
+/// The attributes are complete if all attributes are either a doc comment or a builtin attribute other than `cfg_attr`
+pub fn is_complete(attrs: &[ast::Attribute]) -> bool {
+ attrs.iter().all(|attr| {
+ attr.is_doc_comment()
+ || attr.ident().is_some_and(|ident| {
+ ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name)
+ })
})
}
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 1e6ac5496..b579da098 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -61,8 +61,8 @@ impl AttrWrapper {
self.attrs.is_empty()
}
- pub fn maybe_needs_tokens(&self) -> bool {
- crate::parser::attr::maybe_needs_tokens(&self.attrs)
+ pub fn is_complete(&self) -> bool {
+ crate::parser::attr::is_complete(&self.attrs)
}
}
@@ -201,7 +201,7 @@ impl<'a> Parser<'a> {
// by definition
if matches!(force_collect, ForceCollect::No)
// None of our outer attributes can require tokens (e.g. a proc-macro)
- && !attrs.maybe_needs_tokens()
+ && attrs.is_complete()
// If our target supports custom inner attributes, then we cannot bail
// out early, since we may need to capture tokens for a custom inner attribute
// invocation.
@@ -244,9 +244,9 @@ impl<'a> Parser<'a> {
// Now that we've parsed an AST node, we have more information available.
if matches!(force_collect, ForceCollect::No)
// We now have inner attributes available, so this check is more precise
- // than `attrs.maybe_needs_tokens()` at the start of the function.
+ // than `attrs.is_complete()` at the start of the function.
// As a result, we don't need to check `R::SUPPORTS_CUSTOM_INNER_ATTRS`
- && !crate::parser::attr::maybe_needs_tokens(ret.attrs())
+ && crate::parser::attr::is_complete(ret.attrs())
// Subtle: We call `has_cfg_or_cfg_attr` with the attrs from `ret`.
// This ensures that we consider inner attributes (e.g. `#![cfg]`),
// which require us to have tokens available
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c14540396..0ce6a570d 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -605,6 +605,22 @@ impl<'a> Parser<'a> {
}
}
+ if let TokenKind::Ident(prev, _) = &self.prev_token.kind
+ && let TokenKind::Ident(cur, _) = &self.token.kind
+ {
+ let concat = Symbol::intern(&format!("{}{}", prev, cur));
+ let ident = Ident::new(concat, DUMMY_SP);
+ if ident.is_used_keyword() || ident.is_reserved() || ident.is_raw_guess() {
+ let span = self.prev_token.span.to(self.token.span);
+ err.span_suggestion_verbose(
+ span,
+ format!("consider removing the space to spell keyword `{}`", concat),
+ concat,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
// `pub` may be used for an item or `pub(crate)`
if self.prev_token.is_ident_named(sym::public)
&& (self.token.can_begin_item()
@@ -751,13 +767,24 @@ impl<'a> Parser<'a> {
tail.could_be_bare_literal = true;
if maybe_struct_name.is_ident() && can_be_struct_literal {
// Account for `if Example { a: one(), }.is_pos() {}`.
- Err(self.sess.create_err(StructLiteralNeedingParens {
- span: maybe_struct_name.span.to(expr.span),
- sugg: StructLiteralNeedingParensSugg {
- before: maybe_struct_name.span.shrink_to_lo(),
- after: expr.span.shrink_to_hi(),
- },
- }))
+ // expand `before` so that we take care of module path such as:
+ // `foo::Bar { ... } `
+ // we expect to suggest `(foo::Bar { ... })` instead of `foo::(Bar { ... })`
+ let sm = self.sess.source_map();
+ let before = maybe_struct_name.span.shrink_to_lo();
+ if let Ok(extend_before) = sm.span_extend_prev_while(before, |t| {
+ t.is_alphanumeric() || t == ':' || t == '_'
+ }) {
+ Err(self.sess.create_err(StructLiteralNeedingParens {
+ span: maybe_struct_name.span.to(expr.span),
+ sugg: StructLiteralNeedingParensSugg {
+ before: extend_before.shrink_to_lo(),
+ after: expr.span.shrink_to_hi(),
+ },
+ }))
+ } else {
+ return None;
+ }
} else {
self.sess.emit_err(StructLiteralBodyWithoutPath {
span: expr.span,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 1b28f3c97..7ede4fbc3 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -91,6 +91,18 @@ impl From<P<Expr>> for LhsExpr {
}
}
+#[derive(Debug)]
+enum DestructuredFloat {
+ /// 1e2
+ Single(Symbol, Span),
+ /// 1.
+ TrailingDot(Symbol, Span, Span),
+ /// 1.2 | 1.2e3
+ MiddleDot(Symbol, Span, Span, Symbol, Span),
+ /// Invalid
+ Error,
+}
+
impl<'a> Parser<'a> {
/// Parses an expression.
#[inline]
@@ -1001,9 +1013,15 @@ impl<'a> Parser<'a> {
}
fn error_unexpected_after_dot(&self) {
- // FIXME Could factor this out into non_fatal_unexpected or something.
let actual = pprust::token_to_string(&self.token);
- self.sess.emit_err(errors::UnexpectedTokenAfterDot { span: self.token.span, actual });
+ let span = self.token.span;
+ let sm = self.sess.source_map();
+ let (span, actual) = match (&self.token.kind, self.subparser_name) {
+ (token::Eof, Some(_)) if let Ok(actual) = sm.span_to_snippet(sm.next_point(span)) =>
+ (span.shrink_to_hi(), actual.into()),
+ _ => (span, actual),
+ };
+ self.sess.emit_err(errors::UnexpectedTokenAfterDot { span, actual });
}
// We need an identifier or integer, but the next token is a float.
@@ -1013,13 +1031,8 @@ impl<'a> Parser<'a> {
// support pushing "future tokens" (would be also helpful to `break_and_eat`), or
// we should break everything including floats into more basic proc-macro style
// tokens in the lexer (probably preferable).
- fn parse_expr_tuple_field_access_float(
- &mut self,
- lo: Span,
- base: P<Expr>,
- float: Symbol,
- suffix: Option<Symbol>,
- ) -> P<Expr> {
+ // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`.
+ fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat {
#[derive(Debug)]
enum FloatComponent {
IdentLike(String),
@@ -1056,7 +1069,7 @@ impl<'a> Parser<'a> {
match &*components {
// 1e2
[IdentLike(i)] => {
- self.parse_expr_tuple_field_access(lo, base, Symbol::intern(&i), suffix, None)
+ DestructuredFloat::Single(Symbol::intern(&i), span)
}
// 1.
[IdentLike(i), Punct('.')] => {
@@ -1068,11 +1081,8 @@ impl<'a> Parser<'a> {
} else {
(span, span)
};
- 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), self.token_spacing);
- self.parse_expr_tuple_field_access(lo, base, symbol, None, Some(next_token))
+ DestructuredFloat::TrailingDot(symbol, ident_span, dot_span)
}
// 1.2 | 1.2e3
[IdentLike(i1), Punct('.'), IdentLike(i2)] => {
@@ -1088,16 +1098,8 @@ impl<'a> Parser<'a> {
(span, span, span)
};
let symbol1 = Symbol::intern(&i1);
- self.token = Token::new(token::Ident(symbol1, false), ident1_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_expr_tuple_field_access(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.token_spacing)); // `.`
- self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None)
+ DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span)
}
// 1e+ | 1e- (recovered)
[IdentLike(_), Punct('+' | '-')] |
@@ -1109,12 +1111,83 @@ impl<'a> Parser<'a> {
[IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => {
// See the FIXME about `TokenCursor` above.
self.error_unexpected_after_dot();
- base
+ DestructuredFloat::Error
}
_ => panic!("unexpected components in a float token: {:?}", components),
}
}
+ fn parse_expr_tuple_field_access_float(
+ &mut self,
+ lo: Span,
+ base: P<Expr>,
+ float: Symbol,
+ suffix: Option<Symbol>,
+ ) -> P<Expr> {
+ match self.break_up_float(float) {
+ // 1e2
+ DestructuredFloat::Single(sym, _sp) => {
+ self.parse_expr_tuple_field_access(lo, base, sym, suffix, None)
+ }
+ // 1.
+ DestructuredFloat::TrailingDot(sym, ident_span, dot_span) => {
+ assert!(suffix.is_none());
+ self.token = Token::new(token::Ident(sym, false), ident_span);
+ let next_token = (Token::new(token::Dot, dot_span), self.token_spacing);
+ self.parse_expr_tuple_field_access(lo, base, sym, None, Some(next_token))
+ }
+ // 1.2 | 1.2e3
+ DestructuredFloat::MiddleDot(symbol1, ident1_span, dot_span, symbol2, ident2_span) => {
+ self.token = Token::new(token::Ident(symbol1, false), ident1_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_expr_tuple_field_access(lo, base, symbol1, None, Some(next_token1));
+ let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span);
+ self.bump_with((next_token2, self.token_spacing)); // `.`
+ self.parse_expr_tuple_field_access(lo, base1, symbol2, suffix, None)
+ }
+ DestructuredFloat::Error => base,
+ }
+ }
+
+ fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec<Ident>> {
+ let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind
+ else {
+ return Ok(thin_vec![self.parse_field_name()?]);
+ };
+ Ok(match self.break_up_float(symbol) {
+ // 1e2
+ DestructuredFloat::Single(sym, sp) => {
+ self.bump();
+ thin_vec![Ident::new(sym, sp)]
+ }
+ // 1.
+ DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {
+ assert!(suffix.is_none());
+ // Analogous to `Self::break_and_eat`
+ self.token_cursor.break_last_token = true;
+ // This might work, in cases like `1. 2`, and might not,
+ // in cases like `offset_of!(Ty, 1.)`. It depends on what comes
+ // after the float-like token, and therefore we have to make
+ // the other parts of the parser think that there is a dot literal.
+ self.token = Token::new(token::Ident(sym, false), sym_span);
+ self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));
+ thin_vec![Ident::new(sym, sym_span)]
+ }
+ // 1.2 | 1.2e3
+ DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => {
+ self.bump();
+ thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)]
+ }
+ DestructuredFloat::Error => {
+ self.bump();
+ thin_vec![Ident::new(symbol, self.prev_token.span)]
+ }
+ })
+ }
+
fn parse_expr_tuple_field_access(
&mut self,
lo: Span,
@@ -1363,6 +1436,8 @@ impl<'a> Parser<'a> {
self.parse_expr_yield()
} else if self.is_do_yeet() {
self.parse_expr_yeet()
+ } else if self.eat_keyword(kw::Become) {
+ self.parse_expr_become()
} else if self.check_keyword(kw::Let) {
self.parse_expr_let()
} else if self.eat_keyword(kw::Underscore) {
@@ -1679,6 +1754,16 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(expr)
}
+ /// Parse `"become" expr`, with `"become"` token already eaten.
+ fn parse_expr_become(&mut self) -> PResult<'a, P<Expr>> {
+ let lo = self.prev_token.span;
+ let kind = ExprKind::Become(self.parse_expr()?);
+ let span = lo.to(self.prev_token.span);
+ self.sess.gated_spans.gate(sym::explicit_tail_calls, span);
+ let expr = self.mk_expr(span, kind);
+ self.maybe_recover_from_bad_qpath(expr)
+ }
+
/// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
/// If the label is followed immediately by a `:` token, the label and `:` are
/// parsed as part of the expression (i.e. a labeled loop). The language team has
@@ -1821,10 +1906,11 @@ impl<'a> Parser<'a> {
let (fields, _trailing, _recovered) = self.parse_seq_to_before_end(
&TokenKind::CloseDelim(Delimiter::Parenthesis),
seq_sep,
- Parser::parse_field_name,
+ Parser::parse_field_name_maybe_tuple,
)?;
+ let fields = fields.into_iter().flatten().collect::<Vec<_>>();
let span = lo.to(self.token.span);
- Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.to_vec().into())))
+ Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into())))
}
/// Returns a string literal if the next token is a string literal.
@@ -1955,17 +2041,14 @@ impl<'a> Parser<'a> {
let recovered = self.recover_after_dot();
let token = recovered.as_ref().unwrap_or(&self.token);
match token::Lit::from_token(token) {
- Some(token_lit) => {
- match MetaItemLit::from_token_lit(token_lit, token.span) {
+ Some(lit) => {
+ match MetaItemLit::from_token_lit(lit, token.span) {
Ok(lit) => {
self.bump();
Some(lit)
}
Err(err) => {
- let span = token.span;
- let token::Literal(lit) = token.kind else {
- unreachable!();
- };
+ let span = token.uninterpolated_span();
self.bump();
report_lit_error(&self.sess, err, lit, span);
// Pack possible quotes and prefixes from the original literal into
@@ -2109,6 +2192,10 @@ impl<'a> Parser<'a> {
self.sess.emit_err(errors::InvalidBlockMacroSegment {
span: self.token.span,
context: lo.to(self.token.span),
+ wrap: errors::WrapInExplicitBlock {
+ lo: self.token.span.shrink_to_lo(),
+ hi: self.token.span.shrink_to_hi(),
+ },
});
}
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index cd779b0b4..8ab38c4fb 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -43,6 +43,15 @@ impl<'a> Parser<'a> {
fn parse_ty_param(&mut self, preceding_attrs: AttrVec) -> PResult<'a, GenericParam> {
let ident = self.parse_ident()?;
+ // We might have a typo'd `Const` that was parsed as a type parameter.
+ if self.may_recover()
+ && ident.name.as_str().to_ascii_lowercase() == kw::Const.as_str()
+ && self.check_ident()
+ // `Const` followed by IDENT
+ {
+ return Ok(self.recover_const_param_with_mistyped_const(preceding_attrs, ident)?);
+ }
+
// Parse optional colon and param bounds.
let mut colon_span = None;
let bounds = if self.eat(&token::Colon) {
@@ -120,6 +129,41 @@ impl<'a> Parser<'a> {
})
}
+ pub(crate) fn recover_const_param_with_mistyped_const(
+ &mut self,
+ preceding_attrs: AttrVec,
+ mistyped_const_ident: Ident,
+ ) -> PResult<'a, GenericParam> {
+ let ident = self.parse_ident()?;
+ self.expect(&token::Colon)?;
+ let ty = self.parse_ty()?;
+
+ // Parse optional const generics default value.
+ let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None };
+
+ let mut err = self.struct_span_err(
+ mistyped_const_ident.span,
+ format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()),
+ );
+ err.span_suggestion_verbose(
+ mistyped_const_ident.span,
+ "use the `const` keyword",
+ kw::Const.as_str(),
+ Applicability::MachineApplicable,
+ );
+ err.emit();
+
+ Ok(GenericParam {
+ ident,
+ id: ast::DUMMY_NODE_ID,
+ attrs: preceding_attrs,
+ bounds: Vec::new(),
+ kind: GenericParamKind::Const { ty, kw_span: mistyped_const_ident.span, default },
+ is_placeholder: false,
+ colon_span: None,
+ })
+ }
+
/// Parses a (possibly empty) list of lifetime and type parameters, possibly including
/// a trailing comma and erroneous trailing attributes.
pub(super) fn parse_generic_params(&mut self) -> PResult<'a, ThinVec<ast::GenericParam>> {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 3783ec41b..1470180de 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2182,7 +2182,11 @@ impl<'a> Parser<'a> {
// `extern ABI fn`
|| self.check_keyword_case(kw::Extern, case)
&& self.look_ahead(1, |t| t.can_begin_literal_maybe_minus())
- && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case))
+ && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) ||
+ // this branch is only for better diagnostic in later, `pub` is not allowed here
+ (self.may_recover()
+ && self.look_ahead(2, |t| t.is_keyword(kw::Pub))
+ && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case))))
}
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index c317d9636..fdf365178 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -938,7 +938,8 @@ impl<'a> Parser<'a> {
let mut etc = false;
let mut ate_comma = true;
let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
- let mut etc_span = None;
+ let mut first_etc_and_maybe_comma_span = None;
+ let mut last_non_comma_dotdot_span = None;
while self.token != token::CloseDelim(Delimiter::Brace) {
let attrs = match self.parse_outer_attributes() {
@@ -969,12 +970,27 @@ impl<'a> Parser<'a> {
{
etc = true;
let mut etc_sp = self.token.span;
+ if first_etc_and_maybe_comma_span.is_none() {
+ if let Some(comma_tok) = self
+ .look_ahead(1, |t| if *t == token::Comma { Some(t.clone()) } else { None })
+ {
+ let nw_span = self
+ .sess
+ .source_map()
+ .span_extend_to_line(comma_tok.span)
+ .trim_start(comma_tok.span.shrink_to_lo())
+ .map(|s| self.sess.source_map().span_until_non_whitespace(s));
+ first_etc_and_maybe_comma_span = nw_span.map(|s| etc_sp.to(s));
+ } else {
+ first_etc_and_maybe_comma_span =
+ Some(self.sess.source_map().span_until_non_whitespace(etc_sp));
+ }
+ }
self.recover_bad_dot_dot();
self.bump(); // `..` || `...` || `_`
if self.token == token::CloseDelim(Delimiter::Brace) {
- etc_span = Some(etc_sp);
break;
}
let token_str = super::token_descr(&self.token);
@@ -996,7 +1012,6 @@ impl<'a> Parser<'a> {
ate_comma = true;
}
- etc_span = Some(etc_sp.until(self.token.span));
if self.token == token::CloseDelim(Delimiter::Brace) {
// If the struct looks otherwise well formed, recover and continue.
if let Some(sp) = comma_sp {
@@ -1040,6 +1055,9 @@ impl<'a> Parser<'a> {
}
}?;
ate_comma = this.eat(&token::Comma);
+
+ last_non_comma_dotdot_span = Some(this.prev_token.span);
+
// We just ate a comma, so there's no need to use
// `TrailingToken::Comma`
Ok((field, TrailingToken::None))
@@ -1049,15 +1067,30 @@ impl<'a> Parser<'a> {
}
if let Some(mut err) = delayed_err {
- if let Some(etc_span) = etc_span {
- err.multipart_suggestion(
- "move the `..` to the end of the field list",
- vec![
- (etc_span, String::new()),
- (self.token.span, format!("{}.. }}", if ate_comma { "" } else { ", " })),
- ],
- Applicability::MachineApplicable,
- );
+ if let Some(first_etc_span) = first_etc_and_maybe_comma_span {
+ if self.prev_token == token::DotDot {
+ // We have `.., x, ..`.
+ err.multipart_suggestion(
+ "remove the starting `..`",
+ vec![(first_etc_span, String::new())],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ if let Some(last_non_comma_dotdot_span) = last_non_comma_dotdot_span {
+ // We have `.., x`.
+ err.multipart_suggestion(
+ "move the `..` to the end of the field list",
+ vec![
+ (first_etc_span, String::new()),
+ (
+ self.token.span.to(last_non_comma_dotdot_span.shrink_to_hi()),
+ format!("{} .. }}", if ate_comma { "" } else { "," }),
+ ),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}
err.emit();
}
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 54f9fc5d2..9fcf51a04 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -23,6 +23,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::symbol::{kw, sym, Ident};
+use std::borrow::Cow;
use std::mem;
use thin_vec::{thin_vec, ThinVec};
@@ -364,7 +365,7 @@ impl<'a> Parser<'a> {
// `let...else if`. Emit the same error that `parse_block()` would,
// but explicitly point out that this pattern is not allowed.
let msg = "conditional `else if` is not supported for `let...else`";
- return Err(self.error_block_no_opening_brace_msg(msg));
+ return Err(self.error_block_no_opening_brace_msg(Cow::from(msg)));
}
let els = self.parse_block()?;
self.check_let_else_init_bool_expr(&init);
@@ -438,7 +439,7 @@ impl<'a> Parser<'a> {
fn error_block_no_opening_brace_msg(
&mut self,
- msg: &str,
+ msg: Cow<'static, str>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let sp = self.token.span;
let mut e = self.struct_span_err(sp, msg);
@@ -502,7 +503,7 @@ impl<'a> Parser<'a> {
fn error_block_no_opening_brace<T>(&mut self) -> PResult<'a, T> {
let tok = super::token_descr(&self.token);
let msg = format!("expected `{{`, found {}", tok);
- Err(self.error_block_no_opening_brace_msg(&msg))
+ Err(self.error_block_no_opening_brace_msg(Cow::from(msg)))
}
/// Parses a block. Inner attributes are allowed.
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 7f9222dac..a607e483c 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -102,9 +102,6 @@ passes_const_impl_const_trait =
const `impl`s must be for traits marked with `#[const_trait]`
.note = this trait must be annotated with `#[const_trait]`
-passes_const_trait =
- attribute should be applied to a trait
-
passes_continue_labeled_block =
`continue` pointing to a labeled block
.label = labeled blocks cannot be `continue`'d
@@ -211,6 +208,8 @@ passes_doc_keyword_not_mod =
passes_doc_keyword_only_impl =
`#[doc(keyword = "...")]` should be used on impl blocks
+passes_doc_test_literal = `#![doc(test(...)]` does not take a literal
+
passes_doc_test_takes_list =
`#[doc(test(...)]` takes a list of attributes
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c3189d1fe..073760f39 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -110,9 +110,6 @@ impl CheckAttrVisitor<'_> {
sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
sym::marker => self.check_marker(hir_id, attr, span, target),
- sym::rustc_must_implement_one_of => {
- self.check_rustc_must_implement_one_of(attr, span, target)
- }
sym::target_feature => self.check_target_feature(hir_id, attr, span, target),
sym::thread_local => self.check_thread_local(attr, span, target),
sym::track_caller => {
@@ -159,12 +156,14 @@ impl CheckAttrVisitor<'_> {
| sym::rustc_dirty
| sym::rustc_if_this_changed
| sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(&attr),
- sym::rustc_coinductive => self.check_rustc_coinductive(&attr, span, target),
+ sym::rustc_coinductive
+ | sym::rustc_must_implement_one_of
+ | sym::rustc_deny_explicit_impl
+ | sym::const_trait => self.check_must_be_applied_to_trait(&attr, span, target),
sym::cmse_nonsecure_entry => {
self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
}
sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
- sym::const_trait => self.check_const_trait(attr, span, target),
sym::must_not_suspend => self.check_must_not_suspend(&attr, span, target),
sym::must_use => self.check_must_use(hir_id, &attr, target),
sym::rustc_pass_by_value => self.check_pass_by_value(&attr, span, target),
@@ -567,25 +566,6 @@ impl CheckAttrVisitor<'_> {
}
}
- /// Checks if the `#[rustc_must_implement_one_of]` attribute on a `target` is valid. Returns `true` if valid.
- fn check_rustc_must_implement_one_of(
- &self,
- attr: &Attribute,
- span: Span,
- target: Target,
- ) -> bool {
- match target {
- Target::Trait => true,
- _ => {
- self.tcx.sess.emit_err(errors::AttrShouldBeAppliedToTrait {
- attr_span: attr.span,
- defn_span: span,
- });
- false
- }
- }
- }
-
/// Checks if the `#[target_feature]` attribute on `item` is valid. Returns `true` if valid.
fn check_target_feature(
&self,
@@ -944,21 +924,28 @@ impl CheckAttrVisitor<'_> {
let mut is_valid = true;
if let Some(metas) = meta.meta_item_list() {
for i_meta in metas {
- match i_meta.name_or_empty() {
- sym::attr | sym::no_crate_inject => {}
- _ => {
+ match (i_meta.name_or_empty(), i_meta.meta_item()) {
+ (sym::attr | sym::no_crate_inject, _) => {}
+ (_, Some(m)) => {
self.tcx.emit_spanned_lint(
INVALID_DOC_ATTRIBUTES,
hir_id,
i_meta.span(),
errors::DocTestUnknown {
- path: rustc_ast_pretty::pprust::path_to_string(
- &i_meta.meta_item().unwrap().path,
- ),
+ path: rustc_ast_pretty::pprust::path_to_string(&m.path),
},
);
is_valid = false;
}
+ (_, None) => {
+ self.tcx.emit_spanned_lint(
+ INVALID_DOC_ATTRIBUTES,
+ hir_id,
+ i_meta.span(),
+ errors::DocTestLiteral,
+ );
+ is_valid = false;
+ }
}
}
} else {
@@ -1584,8 +1571,8 @@ impl CheckAttrVisitor<'_> {
}
}
- /// Checks if the `#[rustc_coinductive]` attribute is applied to a trait.
- fn check_rustc_coinductive(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+ /// Checks if the attribute is applied to a trait.
+ fn check_must_be_applied_to_trait(&self, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
Target::Trait => true,
_ => {
@@ -1979,17 +1966,6 @@ impl CheckAttrVisitor<'_> {
}
}
- /// `#[const_trait]` only applies to traits.
- fn check_const_trait(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
- match target {
- Target::Trait => true,
- _ => {
- self.tcx.sess.emit_err(errors::ConstTrait { attr_span: attr.span });
- false
- }
- }
- }
-
fn check_stability_promotable(&self, attr: &Attribute, _span: Span, target: Target) -> bool {
match target {
Target::Expression => {
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 2357b0aad..fc437c429 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -199,6 +199,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
}
+ fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) {
+ let kind = Some(hir::ConstContext::Const);
+ self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block));
+ }
+
fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
let owner = self.tcx.hir().body_owner_def_id(body.id());
let kind = self.tcx.hir().body_const_context(owner);
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 7812dcde4..d5ac1cd9c 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -500,6 +500,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
self.in_pat = in_pat;
}
+
+ fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
+ // When inline const blocks are used in pattern position, paths
+ // referenced by it should be considered as used.
+ let in_pat = mem::replace(&mut self.in_pat, false);
+
+ self.live_symbols.insert(c.def_id);
+ intravisit::walk_inline_const(self, c);
+
+ self.in_pat = in_pat;
+ }
}
fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 99fc69d1b..3fe7feb9d 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -282,6 +282,10 @@ pub struct DocTestUnknown {
}
#[derive(LintDiagnostic)]
+#[diag(passes_doc_test_literal)]
+pub struct DocTestLiteral;
+
+#[derive(LintDiagnostic)]
#[diag(passes_doc_test_takes_list)]
pub struct DocTestTakesList;
@@ -607,13 +611,6 @@ pub struct RustcStdInternalSymbol {
}
#[derive(Diagnostic)]
-#[diag(passes_const_trait)]
-pub struct ConstTrait {
- #[primary_span]
- pub attr_span: Span,
-}
-
-#[derive(Diagnostic)]
#[diag(passes_link_ordinal)]
pub struct LinkOrdinal {
#[primary_span]
@@ -1153,14 +1150,6 @@ pub struct UnixSigpipeValues {
pub span: Span,
}
-#[derive(Diagnostic)]
-#[diag(passes_no_main_function, code = "E0601")]
-pub struct NoMainFunction {
- #[primary_span]
- pub span: Span,
- pub crate_name: String,
-}
-
pub struct NoMainErr {
pub sp: Span,
pub crate_name: Symbol,
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index dc5e45407..6c748147a 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -302,8 +302,8 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
[
ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
- Path, AddrOf, Break, Continue, Ret, InlineAsm, OffsetOf, Struct, Repeat, Yield,
- Err
+ Path, AddrOf, Break, Continue, Ret, Become, InlineAsm, OffsetOf, Struct, Repeat,
+ Yield, Err
]
);
hir_visit::walk_expr(self, e)
@@ -569,7 +569,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
- InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
+ InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
+ Become, IncludedBytes, Err
]
);
ast_visit::walk_expr(self, e)
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 5a1ae808e..098107f8f 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -93,7 +93,8 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
Err(layout_error) => {
tcx.sess.emit_fatal(Spanned {
- node: layout_error,
+ node: layout_error.into_diagnostic(),
+
span: tcx.def_span(item_def_id.to_def_id()),
});
}
@@ -109,12 +110,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for UnwrapLayoutCx<'tcx> {
type LayoutOfResult = TyAndLayout<'tcx>;
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
- span_bug!(
- span,
- "`#[rustc_layout(..)]` test resulted in `layout_of({}) = Err({})`",
- ty,
- err
- );
+ span_bug!(span, "`#[rustc_layout(..)]` test resulted in `layout_of({ty}) = Err({err})`",);
}
}
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 63b1578d4..803ca05b2 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -463,6 +463,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
| hir::ExprKind::Lit(_)
| hir::ExprKind::ConstBlock(..)
| hir::ExprKind::Ret(..)
+ | hir::ExprKind::Become(..)
| hir::ExprKind::Block(..)
| hir::ExprKind::Assign(..)
| hir::ExprKind::AssignOp(..)
@@ -967,6 +968,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_opt_expr(o_e.as_deref(), self.exit_ln)
}
+ hir::ExprKind::Become(ref e) => {
+ // Ignore succ and subst exit_ln.
+ self.propagate_through_expr(e, self.exit_ln)
+ }
+
hir::ExprKind::Break(label, ref opt_expr) => {
// Find which label this break jumps to
let target = match label.target_id {
@@ -1408,6 +1414,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
| hir::ExprKind::DropTemps(..)
| hir::ExprKind::Unary(..)
| hir::ExprKind::Ret(..)
+ | hir::ExprKind::Become(..)
| hir::ExprKind::Break(..)
| hir::ExprKind::Continue(..)
| hir::ExprKind::Lit(_)
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index 73cfe68e7..7c64df6a5 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -24,7 +24,7 @@ enum Context {
Closure(Span),
AsyncClosure(Span),
LabeledBlock,
- AnonConst,
+ Constant,
}
#[derive(Copy, Clone)]
@@ -53,7 +53,11 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
}
fn visit_anon_const(&mut self, c: &'hir hir::AnonConst) {
- self.with_context(AnonConst, |v| intravisit::walk_anon_const(v, c));
+ self.with_context(Constant, |v| intravisit::walk_anon_const(v, c));
+ }
+
+ fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) {
+ self.with_context(Constant, |v| intravisit::walk_inline_const(v, c));
}
fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
@@ -192,7 +196,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
AsyncClosure(closure_span) => {
self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
}
- Normal | AnonConst => {
+ Normal | Constant => {
self.sess.emit_err(OutsideLoop { span, name, is_break: name == "break" });
}
}
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index a849d61ed..769b38900 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -204,6 +204,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
| ExprKind::Continue(..)
| ExprKind::Ret(..)
| ExprKind::OffsetOf(..)
+ | ExprKind::Become(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
| ExprKind::Yield(..) => {
diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl
index b68e8a78a..b91e0d18a 100644
--- a/compiler/rustc_privacy/messages.ftl
+++ b/compiler/rustc_privacy/messages.ftl
@@ -17,7 +17,14 @@ privacy_private_in_public_lint =
*[other] E0446
})
+privacy_private_interface_or_bounds_lint = {$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}`
+ .item_label = {$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}`
+ .ty_note = but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}`
+
privacy_report_effective_visibility = {$descr}
+privacy_unnameable_types_lint = {$kind} `{$descr}` is reachable but cannot be named
+ .label = reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}`
+
privacy_unnamed_item_is_private = {$kind} is private
.label = private {$kind}
diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs
index 72b53eefa..da18f0c82 100644
--- a/compiler/rustc_privacy/src/errors.rs
+++ b/compiler/rustc_privacy/src/errors.rs
@@ -98,3 +98,32 @@ pub struct PrivateInPublicLint<'a> {
pub kind: &'a str,
pub descr: DiagnosticArgFromDisplay<'a>,
}
+
+#[derive(LintDiagnostic)]
+#[diag(privacy_unnameable_types_lint)]
+pub struct UnnameableTypesLint<'a> {
+ #[label]
+ pub span: Span,
+ pub kind: &'a str,
+ pub descr: DiagnosticArgFromDisplay<'a>,
+ pub reachable_vis: &'a str,
+ pub reexported_vis: &'a str,
+}
+
+// Used for `private_interfaces` and `private_bounds` lints.
+// They will replace private-in-public errors and compatibility lints in future.
+// See https://rust-lang.github.io/rfcs/2145-type-privacy.html for more details.
+#[derive(LintDiagnostic)]
+#[diag(privacy_private_interface_or_bounds_lint)]
+pub struct PrivateInterfacesOrBoundsLint<'a> {
+ #[label(privacy_item_label)]
+ pub item_span: Span,
+ pub item_kind: &'a str,
+ pub item_descr: DiagnosticArgFromDisplay<'a>,
+ pub item_vis_descr: &'a str,
+ #[note(privacy_ty_note)]
+ pub ty_span: Span,
+ pub ty_kind: &'a str,
+ pub ty_descr: DiagnosticArgFromDisplay<'a>,
+ pub ty_vis_descr: &'a str,
+}
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 65dfdf31e..4fcee9396 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -22,7 +22,7 @@ use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
+use rustc_hir::{AssocItemKind, ForeignItemKind, HirIdSet, ItemId, Node, PatKind};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
@@ -42,8 +42,8 @@ use std::{fmt, mem};
use errors::{
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
- InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, ReportEffectiveVisibility,
- UnnamedItemIsPrivate,
+ InPublicInterfaceTraits, ItemIsPrivate, PrivateInPublicLint, PrivateInterfacesOrBoundsLint,
+ ReportEffectiveVisibility, UnnameableTypesLint, UnnamedItemIsPrivate,
};
fluent_messages! { "../messages.ftl" }
@@ -52,6 +52,17 @@ fluent_messages! { "../messages.ftl" }
/// Generic infrastructure used to implement specific visitors below.
////////////////////////////////////////////////////////////////////////////////
+struct LazyDefPathStr<'tcx> {
+ def_id: DefId,
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.tcx.def_path_str(self.def_id))
+ }
+}
+
/// 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
@@ -62,14 +73,10 @@ fluent_messages! { "../messages.ftl" }
/// in `impl Trait`, see individual comments in `DefIdVisitorSkeleton::visit_ty`.
trait DefIdVisitor<'tcx> {
type BreakTy = ();
+ const SHALLOW: bool = false;
+ const SKIP_ASSOC_TYS: bool = false;
fn tcx(&self) -> TyCtxt<'tcx>;
- fn shallow(&self) -> bool {
- false
- }
- fn skip_assoc_tys(&self) -> bool {
- false
- }
fn visit_def_id(
&mut self,
def_id: DefId,
@@ -101,7 +108,13 @@ trait DefIdVisitor<'tcx> {
&mut self,
predicates: ty::GenericPredicates<'tcx>,
) -> ControlFlow<Self::BreakTy> {
- self.skeleton().visit_predicates(predicates)
+ self.skeleton().visit_clauses(predicates.predicates)
+ }
+ fn visit_clauses(
+ &mut self,
+ clauses: &[(ty::Clause<'tcx>, Span)],
+ ) -> ControlFlow<Self::BreakTy> {
+ self.skeleton().visit_clauses(clauses)
}
}
@@ -118,11 +131,7 @@ where
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<V::BreakTy> {
let TraitRef { def_id, substs, .. } = trait_ref;
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)
- }
+ if V::SHALLOW { ControlFlow::Continue(()) } else { substs.visit_with(self) }
}
fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> ControlFlow<V::BreakTy> {
@@ -141,56 +150,35 @@ where
)
};
self.visit_trait(trait_ref)?;
- if self.def_id_visitor.shallow() {
+ if V::SHALLOW {
ControlFlow::Continue(())
} else {
assoc_substs.iter().try_for_each(|subst| subst.visit_with(self))
}
}
- fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<V::BreakTy> {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
- trait_ref,
- constness: _,
- polarity: _,
- })) => self.visit_trait(trait_ref),
- ty::PredicateKind::Clause(ty::Clause::Projection(ty::ProjectionPredicate {
- projection_ty,
- term,
- })) => {
+ fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> ControlFlow<V::BreakTy> {
+ match clause.kind().skip_binder() {
+ ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => {
+ self.visit_trait(trait_ref)
+ }
+ ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => {
term.visit_with(self)?;
self.visit_projection_ty(projection_ty)
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
- ty,
- _region,
- ))) => ty.visit_with(self),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::Continue(()),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => ty.visit_with(self),
+ ty::ClauseKind::RegionOutlives(..) => ControlFlow::Continue(()),
+ ty::ClauseKind::ConstArgHasType(ct, ty) => {
ct.visit_with(self)?;
ty.visit_with(self)
}
- ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self),
- ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
-
- ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::Subtype(_)
- | ty::PredicateKind::Coerce(_)
- | ty::PredicateKind::ConstEquate(_, _)
- | ty::PredicateKind::TypeWellFormedFromEnv(_)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..) => bug!("unexpected predicate: {:?}", predicate),
+ ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self),
+ ty::ClauseKind::WellFormed(arg) => arg.visit_with(self),
}
}
- fn visit_predicates(
- &mut self,
- predicates: ty::GenericPredicates<'tcx>,
- ) -> ControlFlow<V::BreakTy> {
- let ty::GenericPredicates { parent: _, predicates } = predicates;
- predicates.iter().try_for_each(|&(predicate, _span)| self.visit_predicate(predicate))
+ fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> ControlFlow<V::BreakTy> {
+ clauses.into_iter().try_for_each(|&(clause, _span)| self.visit_clause(clause))
}
}
@@ -211,7 +199,7 @@ where
| ty::Closure(def_id, ..)
| ty::Generator(def_id, ..) => {
self.def_id_visitor.visit_def_id(def_id, "type", &ty)?;
- if self.def_id_visitor.shallow() {
+ if V::SHALLOW {
return ControlFlow::Continue(());
}
// Default type visitor doesn't visit signatures of fn types.
@@ -231,8 +219,11 @@ where
}
}
}
+ ty::Alias(ty::Weak, alias) => {
+ self.def_id_visitor.visit_def_id(alias.def_id, "type alias", &ty);
+ }
ty::Alias(ty::Projection, proj) => {
- if self.def_id_visitor.skip_assoc_tys() {
+ if V::SKIP_ASSOC_TYS {
// Visitors searching for minimal visibility/reachability want to
// conservatively approximate associated types like `<Type as Trait>::Alias`
// as visible/reachable even if both `Type` and `Trait` are private.
@@ -244,7 +235,7 @@ where
return self.visit_projection_ty(proj);
}
ty::Alias(ty::Inherent, data) => {
- if self.def_id_visitor.skip_assoc_tys() {
+ if V::SKIP_ASSOC_TYS {
// Visitors searching for minimal visibility/reachability want to
// conservatively approximate associated types like `Type::Alias`
// as visible/reachable even if `Type` is private.
@@ -259,18 +250,8 @@ where
&LazyDefPathStr { def_id: data.def_id, tcx },
)?;
- struct LazyDefPathStr<'tcx> {
- def_id: DefId,
- tcx: TyCtxt<'tcx>,
- }
- impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.tcx.def_path_str(self.def_id))
- }
- }
-
// This will also visit substs if necessary, so we don't need to recurse.
- return if self.def_id_visitor.shallow() {
+ return if V::SHALLOW {
ControlFlow::Continue(())
} else {
data.substs.iter().try_for_each(|subst| subst.visit_with(self))
@@ -301,10 +282,7 @@ where
// 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.
- self.visit_predicates(ty::GenericPredicates {
- parent: None,
- predicates: tcx.explicit_item_bounds(def_id).skip_binder(),
- })?;
+ self.visit_clauses(tcx.explicit_item_bounds(def_id).skip_binder())?;
}
}
// These types don't have their own def-ids (but may have subcomponents
@@ -332,11 +310,7 @@ where
}
}
- if self.def_id_visitor.shallow() {
- ControlFlow::Continue(())
- } else {
- ty.super_visit_with(self)
- }
+ if V::SHALLOW { ControlFlow::Continue(()) } else { ty.super_visit_with(self) }
}
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
@@ -353,22 +327,20 @@ fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visib
/// Visitor used to determine impl visibility and reachability.
////////////////////////////////////////////////////////////////////////////////
-struct FindMin<'a, 'tcx, VL: VisibilityLike> {
+struct FindMin<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
min: VL,
}
-impl<'a, 'tcx, VL: VisibilityLike> DefIdVisitor<'tcx> for FindMin<'a, 'tcx, VL> {
+impl<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> DefIdVisitor<'tcx>
+ for FindMin<'a, 'tcx, VL, SHALLOW>
+{
+ const SHALLOW: bool = SHALLOW;
+ const SKIP_ASSOC_TYS: bool = true;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
- fn shallow(&self) -> bool {
- VL::SHALLOW
- }
- fn skip_assoc_tys(&self) -> bool {
- true
- }
fn visit_def_id(
&mut self,
def_id: DefId,
@@ -384,17 +356,19 @@ impl<'a, 'tcx, VL: VisibilityLike> DefIdVisitor<'tcx> for FindMin<'a, 'tcx, VL>
trait VisibilityLike: Sized {
const MAX: Self;
- const SHALLOW: bool = false;
- fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self;
+ fn new_min<const SHALLOW: bool>(
+ find: &FindMin<'_, '_, Self, SHALLOW>,
+ def_id: LocalDefId,
+ ) -> Self;
- // Returns an over-approximation (`skip_assoc_tys` = true) of visibility due to
+ // Returns an over-approximation (`SKIP_ASSOC_TYS` = true) of visibility due to
// associated types for which we can't determine visibility precisely.
- fn of_impl(
+ fn of_impl<const SHALLOW: bool>(
def_id: LocalDefId,
tcx: TyCtxt<'_>,
effective_visibilities: &EffectiveVisibilities,
) -> Self {
- let mut find = FindMin { tcx, effective_visibilities, min: Self::MAX };
+ let mut find = FindMin::<_, SHALLOW> { tcx, effective_visibilities, min: Self::MAX };
find.visit(tcx.type_of(def_id).subst_identity());
if let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
find.visit_trait(trait_ref.subst_identity());
@@ -404,31 +378,28 @@ trait VisibilityLike: Sized {
}
impl VisibilityLike for ty::Visibility {
const MAX: Self = ty::Visibility::Public;
- fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self {
+ fn new_min<const SHALLOW: bool>(
+ find: &FindMin<'_, '_, Self, SHALLOW>,
+ def_id: LocalDefId,
+ ) -> Self {
min(find.tcx.local_visibility(def_id), find.min, find.tcx)
}
}
-impl VisibilityLike for Option<EffectiveVisibility> {
- const MAX: Self = Some(EffectiveVisibility::from_vis(ty::Visibility::Public));
- // Type inference is very smart sometimes.
- // It can make an impl reachable even some components of its type or trait are unreachable.
- // E.g. methods of `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }`
- // can be usable from other crates (#57264). So we skip substs when calculating reachability
- // and consider an impl reachable if its "shallow" type and trait are reachable.
- //
- // The assumption we make here is that type-inference won't let you use an impl without knowing
- // both "shallow" version of its self type and "shallow" version of its trait if it exists
- // (which require reaching the `DefId`s in them).
- const SHALLOW: bool = true;
- fn new_min(find: &FindMin<'_, '_, Self>, def_id: LocalDefId) -> Self {
- if let Some(min) = find.min {
- return find
- .effective_visibilities
- .effective_vis(def_id)
- .map(|eff_vis| min.min(*eff_vis, find.tcx));
- }
- None
+impl VisibilityLike for EffectiveVisibility {
+ const MAX: Self = EffectiveVisibility::from_vis(ty::Visibility::Public);
+ fn new_min<const SHALLOW: bool>(
+ find: &FindMin<'_, '_, Self, SHALLOW>,
+ def_id: LocalDefId,
+ ) -> Self {
+ let effective_vis =
+ find.effective_visibilities.effective_vis(def_id).copied().unwrap_or_else(|| {
+ let private_vis =
+ ty::Visibility::Restricted(find.tcx.parent_module_from_def_id(def_id));
+ EffectiveVisibility::from_vis(private_vis)
+ });
+
+ effective_vis.min(find.min, find.tcx)
}
}
@@ -487,14 +458,14 @@ impl<'tcx> EmbargoVisitor<'tcx> {
&mut self,
def_id: LocalDefId,
inherited_effective_vis: EffectiveVisibility,
- nominal_vis: Option<ty::Visibility>,
+ max_vis: Option<ty::Visibility>,
level: Level,
) {
let private_vis = ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id));
- if Some(private_vis) != nominal_vis {
+ if max_vis != Some(private_vis) {
self.changed |= self.effective_visibilities.update(
def_id,
- nominal_vis,
+ max_vis,
|| private_vis,
inherited_effective_vis,
level,
@@ -751,7 +722,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
reach.generics().predicates();
if trait_item_ref.kind == AssocItemKind::Type
- && !tcx.impl_defaultness(trait_item_ref.id.owner_id).has_value()
+ && !tcx.defaultness(trait_item_ref.id.owner_id).has_value()
{
// No type to visit.
} else {
@@ -766,28 +737,34 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
}
}
hir::ItemKind::Impl(ref impl_) => {
- if let Some(item_ev) = Option::<EffectiveVisibility>::of_impl(
+ // Type inference is very smart sometimes. It can make an impl reachable even some
+ // components of its type or trait are unreachable. E.g. methods of
+ // `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }`
+ // can be usable from other crates (#57264). So we skip substs when calculating
+ // reachability and consider an impl reachable if its "shallow" type and trait are
+ // reachable.
+ //
+ // The assumption we make here is that type-inference won't let you use an impl
+ // without knowing both "shallow" version of its self type and "shallow" version of
+ // its trait if it exists (which require reaching the `DefId`s in them).
+ let item_ev = EffectiveVisibility::of_impl::<true>(
item.owner_id.def_id,
self.tcx,
&self.effective_visibilities,
- ) {
- self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct);
+ );
- self.reach(item.owner_id.def_id, item_ev)
- .generics()
- .predicates()
- .ty()
- .trait_ref();
+ self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct);
- for impl_item_ref in impl_.items {
- let def_id = impl_item_ref.id.owner_id.def_id;
- let nominal_vis =
- impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id));
- self.update_eff_vis(def_id, item_ev, nominal_vis, Level::Direct);
+ self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref();
- if let Some(impl_item_ev) = self.get(def_id) {
- self.reach(def_id, impl_item_ev).generics().predicates().ty();
- }
+ for impl_item_ref in impl_.items {
+ let def_id = impl_item_ref.id.owner_id.def_id;
+ let max_vis =
+ impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id));
+ self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct);
+
+ if let Some(impl_item_ev) = self.get(def_id) {
+ self.reach(def_id, impl_item_ev).generics().predicates().ty();
}
}
}
@@ -902,7 +879,12 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx>
_descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if let Some(def_id) = def_id.as_local() {
- self.ev.update_eff_vis(def_id, self.effective_vis, None, self.level);
+ // All effective visibilities except `reachable_through_impl_trait` are limited to
+ // nominal visibility. If any type or trait is leaked farther than that, it will
+ // produce type privacy errors on any use, so we don't consider it leaked.
+ let max_vis = (self.level != Level::ReachableThroughImplTrait)
+ .then(|| self.ev.tcx.local_visibility(def_id));
+ self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level);
}
ControlFlow::Continue(())
}
@@ -916,6 +898,21 @@ pub struct TestReachabilityVisitor<'tcx, 'a> {
effective_visibilities: &'a EffectiveVisibilities,
}
+fn vis_to_string<'tcx>(def_id: LocalDefId, vis: ty::Visibility, tcx: TyCtxt<'tcx>) -> String {
+ match vis {
+ ty::Visibility::Restricted(restricted_id) => {
+ if restricted_id.is_top_level_module() {
+ "pub(crate)".to_string()
+ } else if restricted_id == tcx.parent_module_from_def_id(def_id) {
+ "pub(self)".to_string()
+ } else {
+ format!("pub({})", tcx.item_name(restricted_id.to_def_id()))
+ }
+ }
+ ty::Visibility::Public => "pub".to_string(),
+ }
+}
+
impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
fn effective_visibility_diagnostic(&mut self, def_id: LocalDefId) {
if self.tcx.has_attr(def_id, sym::rustc_effective_visibility) {
@@ -923,18 +920,7 @@ impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
let span = self.tcx.def_span(def_id.to_def_id());
if let Some(effective_vis) = self.effective_visibilities.effective_vis(def_id) {
for level in Level::all_levels() {
- let vis_str = match effective_vis.at_level(level) {
- ty::Visibility::Restricted(restricted_id) => {
- if restricted_id.is_top_level_module() {
- "pub(crate)".to_string()
- } else if *restricted_id == self.tcx.parent_module_from_def_id(def_id) {
- "pub(self)".to_string()
- } else {
- format!("pub({})", self.tcx.item_name(restricted_id.to_def_id()))
- }
- }
- ty::Visibility::Public => "pub".to_string(),
- };
+ let vis_str = vis_to_string(def_id, *effective_vis.at_level(level), self.tcx);
if level != Level::Direct {
error_msg.push_str(", ");
}
@@ -1246,14 +1232,14 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
self.tcx.types.never,
);
- for (pred, _) in bounds.predicates() {
- match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ for (clause, _) in bounds.clauses() {
+ match clause.kind().skip_binder() {
+ ty::ClauseKind::Trait(trait_predicate) => {
if self.visit_trait(trait_predicate.trait_ref).is_break() {
return;
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) => {
+ ty::ClauseKind::Projection(proj_predicate) => {
let term = self.visit(proj_predicate.term);
if term.is_break()
|| self.visit_projection_ty(proj_predicate.projection_ty).is_break()
@@ -1749,12 +1735,15 @@ struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
item_def_id: LocalDefId,
/// The visitor checks that each component type is at least this visible.
required_visibility: ty::Visibility,
+ required_effective_vis: Option<EffectiveVisibility>,
has_old_errors: bool,
in_assoc_ty: bool,
+ in_primary_interface: bool,
}
impl SearchInterfaceForPrivateItemsVisitor<'_> {
fn generics(&mut self) -> &mut Self {
+ self.in_primary_interface = true;
for param in &self.tcx.generics_of(self.item_def_id).params {
match param.kind {
GenericParamDefKind::Lifetime => {}
@@ -1773,6 +1762,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
}
fn predicates(&mut self) -> &mut Self {
+ self.in_primary_interface = false;
// N.B., we use `explicit_predicates_of` and not `predicates_of`
// because we don't want to report privacy errors due to where
// clauses that the compiler inferred. We only want to
@@ -1784,14 +1774,13 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
}
fn bounds(&mut self) -> &mut Self {
- self.visit_predicates(ty::GenericPredicates {
- parent: None,
- predicates: self.tcx.explicit_item_bounds(self.item_def_id).skip_binder(),
- });
+ self.in_primary_interface = false;
+ self.visit_clauses(self.tcx.explicit_item_bounds(self.item_def_id).skip_binder());
self
}
fn ty(&mut self) -> &mut Self {
+ self.in_primary_interface = true;
self.visit(self.tcx.type_of(self.item_def_id).subst_identity());
self
}
@@ -1815,8 +1804,10 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
};
let vis = self.tcx.local_visibility(local_def_id);
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ let span = self.tcx.def_span(self.item_def_id.to_def_id());
+ let vis_span = self.tcx.def_span(def_id);
if !vis.is_at_least(self.required_visibility, self.tcx) {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
let vis_descr = match vis {
ty::Visibility::Public => "public",
ty::Visibility::Restricted(vis_def_id) => {
@@ -1829,12 +1820,11 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
}
}
};
- let span = self.tcx.def_span(self.item_def_id.to_def_id());
+
if self.has_old_errors
|| self.in_assoc_ty
|| self.tcx.resolutions(()).has_pub_restricted
{
- let vis_span = self.tcx.def_span(def_id);
if kind == "trait" {
self.tcx.sess.emit_err(InPublicInterfaceTraits {
span,
@@ -1862,6 +1852,39 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> {
}
}
+ let Some(effective_vis) = self.required_effective_vis else {
+ return false;
+ };
+
+ let reachable_at_vis = *effective_vis.at_level(Level::Reachable);
+
+ if !vis.is_at_least(reachable_at_vis, self.tcx) {
+ let lint = if self.in_primary_interface {
+ lint::builtin::PRIVATE_INTERFACES
+ } else {
+ lint::builtin::PRIVATE_BOUNDS
+ };
+ self.tcx.emit_spanned_lint(
+ lint,
+ hir_id,
+ span,
+ PrivateInterfacesOrBoundsLint {
+ item_span: span,
+ item_kind: self.tcx.def_descr(self.item_def_id.to_def_id()),
+ item_descr: (&LazyDefPathStr {
+ def_id: self.item_def_id.to_def_id(),
+ tcx: self.tcx,
+ })
+ .into(),
+ item_vis_descr: &vis_to_string(self.item_def_id, reachable_at_vis, self.tcx),
+ ty_span: vis_span,
+ ty_kind: kind,
+ ty_descr: descr.into(),
+ ty_vis_descr: &vis_to_string(local_def_id, vis, self.tcx),
+ },
+ );
+ }
+
false
}
@@ -1895,25 +1918,55 @@ impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
}
}
-struct PrivateItemsInPublicInterfacesChecker<'tcx> {
+struct PrivateItemsInPublicInterfacesChecker<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
old_error_set_ancestry: HirIdSet,
+ effective_visibilities: &'a EffectiveVisibilities,
}
-impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
+impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
fn check(
&self,
def_id: LocalDefId,
required_visibility: ty::Visibility,
+ required_effective_vis: Option<EffectiveVisibility>,
) -> SearchInterfaceForPrivateItemsVisitor<'tcx> {
SearchInterfaceForPrivateItemsVisitor {
tcx: self.tcx,
item_def_id: def_id,
required_visibility,
+ required_effective_vis,
has_old_errors: self
.old_error_set_ancestry
.contains(&self.tcx.hir().local_def_id_to_hir_id(def_id)),
in_assoc_ty: false,
+ in_primary_interface: true,
+ }
+ }
+
+ fn check_unnameable(&self, def_id: LocalDefId, effective_vis: Option<EffectiveVisibility>) {
+ let Some(effective_vis) = effective_vis else {
+ return;
+ };
+
+ let reexported_at_vis = effective_vis.at_level(Level::Reexported);
+ let reachable_at_vis = effective_vis.at_level(Level::Reachable);
+
+ if reexported_at_vis != reachable_at_vis {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ let span = self.tcx.def_span(def_id.to_def_id());
+ self.tcx.emit_spanned_lint(
+ lint::builtin::UNNAMEABLE_TYPES,
+ hir_id,
+ span,
+ UnnameableTypesLint {
+ span,
+ kind: self.tcx.def_descr(def_id.to_def_id()),
+ descr: (&LazyDefPathStr { def_id: def_id.to_def_id(), tcx: self.tcx }).into(),
+ reachable_vis: &vis_to_string(def_id, *reachable_at_vis, self.tcx),
+ reexported_vis: &vis_to_string(def_id, *reexported_at_vis, self.tcx),
+ },
+ );
}
}
@@ -1922,13 +1975,19 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
def_id: LocalDefId,
assoc_item_kind: AssocItemKind,
vis: ty::Visibility,
+ effective_vis: Option<EffectiveVisibility>,
) {
- let mut check = self.check(def_id, vis);
+ let mut check = self.check(def_id, vis, effective_vis);
let (check_ty, is_assoc_ty) = match assoc_item_kind {
AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false),
- AssocItemKind::Type => (self.tcx.impl_defaultness(def_id).has_value(), true),
+ AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true),
};
+
+ if is_assoc_ty {
+ self.check_unnameable(def_id, self.get(def_id));
+ }
+
check.in_assoc_ty = is_assoc_ty;
check.generics().predicates();
if check_ty {
@@ -1936,50 +1995,72 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
}
}
+ fn get(&self, def_id: LocalDefId) -> Option<EffectiveVisibility> {
+ self.effective_visibilities.effective_vis(def_id).copied()
+ }
+
pub fn check_item(&mut self, id: ItemId) {
let tcx = self.tcx;
let def_id = id.owner_id.def_id;
let item_visibility = tcx.local_visibility(def_id);
+ let effective_vis = self.get(def_id);
let def_kind = tcx.def_kind(def_id);
match def_kind {
DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => {
- self.check(def_id, item_visibility).generics().predicates().ty();
+ if let DefKind::TyAlias = def_kind {
+ self.check_unnameable(def_id, effective_vis);
+ }
+ self.check(def_id, item_visibility, effective_vis).generics().predicates().ty();
}
DefKind::OpaqueTy => {
// `ty()` for opaque types is the underlying type,
// it's not a part of interface, so we skip it.
- self.check(def_id, item_visibility).generics().bounds();
+ self.check(def_id, item_visibility, effective_vis).generics().bounds();
}
DefKind::Trait => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind {
- self.check(item.owner_id.def_id, item_visibility).generics().predicates();
+ self.check_unnameable(item.owner_id.def_id, effective_vis);
+
+ self.check(item.owner_id.def_id, item_visibility, effective_vis)
+ .generics()
+ .predicates();
for trait_item_ref in trait_item_refs {
self.check_assoc_item(
trait_item_ref.id.owner_id.def_id,
trait_item_ref.kind,
item_visibility,
+ effective_vis,
);
if let AssocItemKind::Type = trait_item_ref.kind {
- self.check(trait_item_ref.id.owner_id.def_id, item_visibility).bounds();
+ self.check(
+ trait_item_ref.id.owner_id.def_id,
+ item_visibility,
+ effective_vis,
+ )
+ .bounds();
}
}
}
}
DefKind::TraitAlias => {
- self.check(def_id, item_visibility).generics().predicates();
+ self.check(def_id, item_visibility, effective_vis).generics().predicates();
}
DefKind::Enum => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Enum(ref def, _) = item.kind {
- self.check(item.owner_id.def_id, item_visibility).generics().predicates();
+ self.check_unnameable(item.owner_id.def_id, effective_vis);
+
+ self.check(item.owner_id.def_id, item_visibility, effective_vis)
+ .generics()
+ .predicates();
for variant in def.variants {
for field in variant.data.fields() {
- self.check(field.def_id, item_visibility).ty();
+ self.check(field.def_id, item_visibility, effective_vis).ty();
}
}
}
@@ -1989,8 +2070,16 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
let item = tcx.hir().item(id);
if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
for foreign_item in items {
- let vis = tcx.local_visibility(foreign_item.id.owner_id.def_id);
- self.check(foreign_item.id.owner_id.def_id, vis)
+ let foreign_item = tcx.hir().foreign_item(foreign_item.id);
+
+ let ev = self.get(foreign_item.owner_id.def_id);
+ let vis = tcx.local_visibility(foreign_item.owner_id.def_id);
+
+ if let ForeignItemKind::Type = foreign_item.kind {
+ self.check_unnameable(foreign_item.owner_id.def_id, ev);
+ }
+
+ self.check(foreign_item.owner_id.def_id, vis, ev)
.generics()
.predicates()
.ty();
@@ -2003,11 +2092,21 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
if let hir::ItemKind::Struct(ref struct_def, _)
| hir::ItemKind::Union(ref struct_def, _) = item.kind
{
- self.check(item.owner_id.def_id, item_visibility).generics().predicates();
+ self.check_unnameable(item.owner_id.def_id, effective_vis);
+ self.check(item.owner_id.def_id, item_visibility, effective_vis)
+ .generics()
+ .predicates();
for field in struct_def.fields() {
let field_visibility = tcx.local_visibility(field.def_id);
- self.check(field.def_id, min(item_visibility, field_visibility, tcx)).ty();
+ let field_ev = self.get(field.def_id);
+
+ self.check(
+ field.def_id,
+ min(item_visibility, field_visibility, tcx),
+ field_ev,
+ )
+ .ty();
}
}
}
@@ -2018,12 +2117,35 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
DefKind::Impl { .. } => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Impl(ref impl_) = item.kind {
- let impl_vis =
- ty::Visibility::of_impl(item.owner_id.def_id, tcx, &Default::default());
+ let impl_vis = ty::Visibility::of_impl::<false>(
+ item.owner_id.def_id,
+ tcx,
+ &Default::default(),
+ );
+
+ // We are using the non-shallow version here, unlike when building the
+ // effective visisibilities table to avoid large number of false positives.
+ // For example in
+ //
+ // impl From<Priv> for Pub {
+ // fn from(_: Priv) -> Pub {...}
+ // }
+ //
+ // lints shouldn't be emmited even if `from` effective visibility
+ // is larger than `Priv` nominal visibility and if `Priv` can leak
+ // in some scenarios due to type inference.
+ let impl_ev = EffectiveVisibility::of_impl::<false>(
+ item.owner_id.def_id,
+ tcx,
+ self.effective_visibilities,
+ );
+
// check that private components do not appear in the generics or predicates of inherent impls
// this check is intentionally NOT performed for impls of traits, per #90586
if impl_.of_trait.is_none() {
- self.check(item.owner_id.def_id, impl_vis).generics().predicates();
+ self.check(item.owner_id.def_id, impl_vis, Some(impl_ev))
+ .generics()
+ .predicates();
}
for impl_item_ref in impl_.items {
let impl_item_vis = if impl_.of_trait.is_none() {
@@ -2035,10 +2157,19 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
} else {
impl_vis
};
+
+ let impl_item_ev = if impl_.of_trait.is_none() {
+ self.get(impl_item_ref.id.owner_id.def_id)
+ .map(|ev| ev.min(impl_ev, self.tcx))
+ } else {
+ Some(impl_ev)
+ };
+
self.check_assoc_item(
impl_item_ref.id.owner_id.def_id,
impl_item_ref.kind,
impl_item_vis,
+ impl_item_ev,
);
}
}
@@ -2137,7 +2268,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
changed: false,
};
- visitor.effective_visibilities.check_invariants(tcx, true);
+ visitor.effective_visibilities.check_invariants(tcx);
if visitor.impl_trait_pass {
// Underlying types of `impl Trait`s are marked as reachable unconditionally,
// so this pass doesn't need to be a part of the fixed point iteration below.
@@ -2154,7 +2285,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
break;
}
}
- visitor.effective_visibilities.check_invariants(tcx, false);
+ visitor.effective_visibilities.check_invariants(tcx);
let mut check_visitor =
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };
@@ -2190,7 +2321,11 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
}
// Check for private types and traits in public interfaces.
- let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, old_error_set_ancestry };
+ let mut checker = PrivateItemsInPublicInterfacesChecker {
+ tcx,
+ old_error_set_ancestry,
+ effective_visibilities,
+ };
for id in tcx.hir().items() {
checker.check_item(id);
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index e59699346..ac697a3ae 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
[dependencies]
-memoffset = { version = "0.6.0", features = ["unstable_const"] }
field-offset = "0.3.5"
measureme = "10.0.0"
rustc_ast = { path = "../rustc_ast" }
@@ -25,5 +24,8 @@ rustc_span = { path = "../rustc_span" }
thin-vec = "0.2.12"
tracing = "0.1"
+# Not used directly, but included to enable the unstable_offset_of feature
+memoffset = { version = "0.9.0", features = ["unstable_offset_of"] }
+
[features]
rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"]
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 244f0e84b..12a3f2ac8 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -16,7 +16,7 @@ use rustc_middle::query::on_disk_cache::AbsoluteBytePos;
use rustc_middle::query::on_disk_cache::{CacheDecoder, CacheEncoder, EncodedDepNodeIndex};
use rustc_middle::query::Key;
use rustc_middle::ty::tls::{self, ImplicitCtxt};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, print::with_no_queries, TyCtxt};
use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext};
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
@@ -312,7 +312,7 @@ pub(crate) fn create_query_frame<
);
let description =
if tcx.sess.verbose() { format!("{description} [{name:?}]") } else { description };
- let span = if kind == dep_graph::DepKind::def_span {
+ let span = if kind == dep_graph::DepKind::def_span || with_no_queries() {
// The `def_span` query is used to calculate `default_span`,
// so exit to avoid infinite recursion.
None
@@ -320,7 +320,7 @@ pub(crate) fn create_query_frame<
Some(key.default_span(tcx))
};
let def_id = key.key_as_def_id();
- let def_kind = if kind == dep_graph::DepKind::opt_def_kind {
+ let def_kind = if kind == dep_graph::DepKind::opt_def_kind || with_no_queries() {
// Try to avoid infinite recursion.
None
} else {
@@ -531,6 +531,8 @@ macro_rules! define_queries {
key: queries::$name::Key<'tcx>,
mode: QueryMode,
) -> Option<Erase<queries::$name::Value<'tcx>>> {
+ #[cfg(debug_assertions)]
+ let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
get_query_incr(
QueryType::config(tcx),
QueryCtxt::new(tcx),
@@ -571,10 +573,16 @@ macro_rules! define_queries {
cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key),
execute_query: |tcx, key| erase(tcx.$name(key)),
compute: |tcx, key| {
+ #[cfg(debug_assertions)]
+ let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
__rust_begin_short_backtrace(||
queries::$name::provided_to_erased(
tcx,
- call_provider!([$($modifiers)*][tcx, $name, key])
+ {
+ let ret = call_provider!([$($modifiers)*][tcx, $name, key]);
+ tracing::trace!(?ret);
+ ret
+ }
)
)
},
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 9e1ca6ab5..39a4cb1b1 100644
--- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs
@@ -46,7 +46,7 @@ use super::{DepContext, DepKind, FingerprintStyle};
use crate::ich::StableHashingContext;
use rustc_data_structures::fingerprint::{Fingerprint, PackedFingerprint};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey};
use rustc_hir::definitions::DefPathHash;
use std::fmt;
use std::hash::Hash;
@@ -247,3 +247,14 @@ impl<HCX> HashStable<HCX> for WorkProductId {
self.hash.hash_stable(hcx, hasher)
}
}
+impl<HCX> ToStableHashKey<HCX> for WorkProductId {
+ type KeyType = Fingerprint;
+ #[inline]
+ fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
+ self.hash
+ }
+}
+unsafe impl StableOrd for WorkProductId {
+ // Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index c0d7386dd..c9e80a6d9 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -1,11 +1,12 @@
use parking_lot::Mutex;
use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef};
use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
+use rustc_data_structures::unord::UnordMap;
use rustc_index::IndexVec;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use smallvec::{smallvec, SmallVec};
@@ -93,7 +94,7 @@ pub struct DepGraphData<K: DepKind> {
/// things available to us. If we find that they are not dirty, we
/// load the path to the file storing those work-products here into
/// this map. We can later look for and extract that data.
- previous_work_products: FxHashMap<WorkProductId, WorkProduct>,
+ previous_work_products: FxIndexMap<WorkProductId, WorkProduct>,
dep_node_debug: Lock<FxHashMap<DepNode<K>, String>>,
@@ -116,7 +117,7 @@ impl<K: DepKind> DepGraph<K> {
pub fn new(
profiler: &SelfProfilerRef,
prev_graph: SerializedDepGraph<K>,
- prev_work_products: FxHashMap<WorkProductId, WorkProduct>,
+ prev_work_products: FxIndexMap<WorkProductId, WorkProduct>,
encoder: FileEncoder,
record_graph: bool,
record_stats: bool,
@@ -688,7 +689,7 @@ impl<K: DepKind> DepGraph<K> {
/// Access the map of work-products created during the cached run. Only
/// used during saving of the dep-graph.
- pub fn previous_work_products(&self) -> &FxHashMap<WorkProductId, WorkProduct> {
+ pub fn previous_work_products(&self) -> &FxIndexMap<WorkProductId, WorkProduct> {
&self.data.as_ref().unwrap().previous_work_products
}
@@ -1048,7 +1049,7 @@ pub struct WorkProduct {
///
/// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to
/// the object file's path, and "dwo" to the dwarf object file's path.
- pub saved_files: FxHashMap<String, String>,
+ pub saved_files: UnordMap<String, String>,
}
// Index type for `DepNodeData`'s edges.
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 730e4c8d3..4adb4eb74 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -69,6 +69,8 @@ where
make_query: fn(Qcx, K) -> QueryStackFrame<D>,
jobs: &mut QueryMap<D>,
) -> Option<()> {
+ let mut active = Vec::new();
+
#[cfg(parallel_compiler)]
{
// We use try_lock_shards here since we are called from the
@@ -77,8 +79,7 @@ where
for shard in shards.iter() {
for (k, v) in shard.iter() {
if let QueryResult::Started(ref job) = *v {
- let query = make_query(qcx, *k);
- jobs.insert(job.id, QueryJobInfo { query, job: job.clone() });
+ active.push((*k, job.clone()));
}
}
}
@@ -91,12 +92,18 @@ where
// really hurt much.)
for (k, v) in self.active.try_lock()?.iter() {
if let QueryResult::Started(ref job) = *v {
- let query = make_query(qcx, *k);
- jobs.insert(job.id, QueryJobInfo { query, job: job.clone() });
+ active.push((*k, job.clone()));
}
}
}
+ // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call
+ // queries leading to a deadlock.
+ for (key, job) in active {
+ let query = make_query(qcx, key);
+ jobs.insert(job.id, QueryJobInfo { query, job });
+ }
+
Some(())
}
}
@@ -119,18 +126,13 @@ where
#[cold]
#[inline(never)]
-fn mk_cycle<Q, Qcx>(
- query: Q,
- qcx: Qcx,
- cycle_error: CycleError<Qcx::DepKind>,
- handler: HandleCycleError,
-) -> Q::Value
+fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError<Qcx::DepKind>) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
- handle_cycle_error(query, qcx, &cycle_error, error, handler)
+ handle_cycle_error(query, qcx, &cycle_error, error)
}
fn handle_cycle_error<Q, Qcx>(
@@ -138,14 +140,13 @@ fn handle_cycle_error<Q, Qcx>(
qcx: Qcx,
cycle_error: &CycleError<Qcx::DepKind>,
mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
- handler: HandleCycleError,
) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
{
use HandleCycleError::*;
- match handler {
+ match query.handle_cycle_error() {
Error => {
error.emit();
query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle)
@@ -270,7 +271,7 @@ where
&qcx.current_query_job(),
span,
);
- (mk_cycle(query, qcx, error, query.handle_cycle_error()), None)
+ (mk_cycle(query, qcx, error), None)
}
#[inline(always)]
@@ -307,7 +308,7 @@ where
(v, Some(index))
}
- Err(cycle) => (mk_cycle(query, qcx, cycle, query.handle_cycle_error()), None),
+ Err(cycle) => (mk_cycle(query, qcx, cycle), None),
}
}
diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs
index b6e2cfa3d..ce551078c 100644
--- a/compiler/rustc_query_system/src/values.rs
+++ b/compiler/rustc_query_system/src/values.rs
@@ -6,10 +6,13 @@ pub trait Value<Tcx: DepContext, D: DepKind>: Sized {
}
impl<Tcx: DepContext, T, D: DepKind> Value<Tcx, D> for T {
- default fn from_cycle_error(tcx: Tcx, _: &[QueryInfo<D>]) -> T {
+ default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> T {
tcx.sess().abort_if_errors();
// Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
// non-trivial to define it earlier.
- panic!("Value::from_cycle_error called without errors");
+ panic!(
+ "<{} as Value>::from_cycle_error called without errors: {cycle:#?}",
+ std::any::type_name::<T>()
+ );
}
}
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index 539b88aa9..f98918cba 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -5,6 +5,9 @@ resolve_add_as_non_derive =
add as non-Derive macro
`#[{$macro_path}]`
+resolve_added_macro_use =
+ have you added the `#[macro_use]` on the module/import?
+
resolve_ampersand_used_without_explicit_lifetime_name =
`&` without an explicit lifetime name cannot be used here
.note = explicit lifetime name needed here
@@ -41,13 +44,44 @@ resolve_binding_shadows_something_unacceptable =
resolve_binding_shadows_something_unacceptable_suggestion =
try specify the pattern arguments
+resolve_cannot_be_reexported_crate_public =
+ `{$ident}` is only public within the crate, and cannot be re-exported outside
+
+resolve_cannot_be_reexported_private =
+ `{$ident}` is private, and cannot be re-exported
+
resolve_cannot_capture_dynamic_environment_in_fn_item =
can't capture dynamic environment in a fn item
.help = use the `|| {"{"} ... {"}"}` closure form instead
+resolve_cannot_determine_import_resolution =
+ cannot determine resolution for the import
+ .note = import resolution is stuck, try simplifying other imports
+
+resolve_cannot_find_ident_in_this_scope =
+ cannot find {$expected} `{$ident}` in this scope
+
+resolve_cannot_glob_import_possible_crates =
+ cannot glob-import all possible crates
+
resolve_cannot_use_self_type_here =
can't use `Self` here
+resolve_change_import_binding =
+ you can use `as` to change the binding name of the import
+
+resolve_consider_adding_a_derive =
+ consider adding a derive
+
+resolve_consider_adding_macro_export =
+ consider adding a `#[macro_export]` to the macro in the imported module
+
+resolve_consider_declaring_with_pub =
+ consider declaring type or module `{$ident}` with `pub`
+
+resolve_consider_marking_as_pub =
+ consider marking `{$ident}` as `pub` in the imported module
+
resolve_const_not_member_of_trait =
const `{$const_}` is not a member of trait `{$trait_}`
.label = not a member of trait `{$trait_}`
@@ -74,6 +108,9 @@ resolve_expected_found =
expected module, found {$res} `{$path_str}`
.label = not a module
+resolve_explicit_unsafe_traits =
+ unsafe traits like `{$ident}` should be implemented explicitly
+
resolve_forward_declared_generic_param =
generic parameters with a default cannot use forward declared identifiers
.label = defaulted generic parameters cannot be forward declared
@@ -83,6 +120,9 @@ resolve_generic_params_from_outer_function =
.label = use of generic parameter from outer function
.suggestion = try using a local generic parameter instead
+resolve_glob_import_doesnt_reexport =
+ glob import doesn't reexport anything because no candidate is public enough
+
resolve_help_try_using_local_generic_param =
try using a local generic parameter instead
@@ -96,6 +136,9 @@ resolve_ident_bound_more_than_once_in_same_pattern =
resolve_imported_crate = `$crate` may not be imported
+resolve_imports_cannot_refer_to =
+ imports cannot refer to {$what}
+
resolve_indeterminate =
cannot determine resolution for the visibility
@@ -104,6 +147,13 @@ resolve_invalid_asm_sym =
.label = is a local variable
.help = `sym` operands must refer to either a function or a static
+resolve_is_not_directly_importable =
+ `{$target}` is not directly importable
+ .label = cannot be imported directly
+
+resolve_items_in_traits_are_not_importable =
+ items in traits are not importable
+
resolve_label_with_similar_name_reachable =
a label with a similar name is reachable
@@ -158,6 +208,12 @@ resolve_parent_module_reset_for_binding =
resolve_proc_macro_same_crate = can't use a procedural macro from the same crate that defines it
.help = you can define integration tests in a directory named `tests`
+resolve_reexport_of_crate_public =
+ re-export of crate public `{$ident}`
+
+resolve_reexport_of_private =
+ re-export of private `{$ident}`
+
resolve_relative_2018 =
relative paths are not supported in visibilities in 2018 edition or later
.suggestion = try
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 727777333..e6ceedddf 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -6,10 +6,10 @@
//! Imports are also considered items and placed into modules here, but not resolved yet.
use crate::def_collector::collect_definitions;
-use crate::imports::{Import, ImportKind};
+use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
-use crate::{errors, BindingKey, MacroData};
+use crate::{errors, BindingKey, MacroData, NameBindingData};
use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
@@ -31,15 +31,14 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use std::cell::Cell;
-use std::ptr;
type Res = def::Res<NodeId>;
impl<'a, Id: Into<DefId>> ToNameBinding<'a>
for (Module<'a>, ty::Visibility<Id>, Span, LocalExpnId)
{
- fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
- arenas.alloc_name_binding(NameBinding {
+ fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+ arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Module(self.0),
ambiguity: None,
vis: self.1.to_def_id(),
@@ -50,8 +49,8 @@ impl<'a, Id: Into<DefId>> ToNameBinding<'a>
}
impl<'a, Id: Into<DefId>> ToNameBinding<'a> for (Res, ty::Visibility<Id>, Span, LocalExpnId) {
- fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
- arenas.alloc_name_binding(NameBinding {
+ fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+ arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Res(self.0),
ambiguity: None,
vis: self.1.to_def_id(),
@@ -71,7 +70,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let binding = def.to_name_binding(self.arenas);
let key = self.new_disambiguated_key(ident, ns);
if let Err(old_binding) = self.try_define(parent, key, binding) {
- self.report_conflict(parent, ident, ns, old_binding, &binding);
+ self.report_conflict(parent, ident, ns, old_binding, binding);
}
}
@@ -142,8 +141,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
Some(def_id) => self.macro_def_scope(def_id),
None => expn_id
.as_local()
- .and_then(|expn_id| self.ast_transform_scopes.get(&expn_id))
- .unwrap_or(&self.graph_root),
+ .and_then(|expn_id| self.ast_transform_scopes.get(&expn_id).copied())
+ .unwrap_or(self.graph_root),
}
}
@@ -354,7 +353,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
vis: ty::Visibility,
) {
let current_module = self.parent_scope.module;
- let import = self.r.arenas.alloc_import(Import {
+ let import = self.r.arenas.alloc_import(ImportData {
kind,
parent_scope: self.parent_scope,
module_path,
@@ -378,7 +377,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
if !type_ns_only || ns == TypeNS {
let key = BindingKey::new(target, ns);
let mut resolution = this.resolution(current_module, key).borrow_mut();
- resolution.add_single_import(import);
+ resolution.single_imports.insert(import);
}
});
}
@@ -848,7 +847,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
(used, Some(ModuleOrUniformRoot::Module(module)), binding)
})
.unwrap_or((true, None, self.r.dummy_binding));
- let import = self.r.arenas.alloc_import(Import {
+ let import = self.r.arenas.alloc_import(ImportData {
kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
root_id: item.id,
parent_scope: self.parent_scope,
@@ -864,7 +863,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
});
self.r.potentially_unused_imports.push(import);
let imported_binding = self.r.import(binding, import);
- if ptr::eq(parent, self.r.graph_root) {
+ if parent == self.r.graph_root {
if let Some(entry) = self.r.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
if expansion != LocalExpnId::ROOT
&& orig_name.is_some()
@@ -996,7 +995,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
fn add_macro_use_binding(
&mut self,
name: Symbol,
- binding: &'a NameBinding<'a>,
+ binding: NameBinding<'a>,
span: Span,
allow_shadowing: bool,
) {
@@ -1058,7 +1057,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
}
let macro_use_import = |this: &Self, span| {
- this.r.arenas.alloc_import(Import {
+ this.r.arenas.alloc_import(ImportData {
kind: ImportKind::MacroUse,
root_id: item.id,
parent_scope: this.parent_scope,
@@ -1228,7 +1227,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
self.r.set_binding_parent_module(binding, parent_scope.module);
self.r.all_macro_rules.insert(ident.name, res);
if is_macro_export {
- let import = self.r.arenas.alloc_import(Import {
+ let import = self.r.arenas.alloc_import(ImportData {
kind: ImportKind::MacroExport,
root_id: item.id,
parent_scope: self.parent_scope,
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 8c6ac822a..d3dcdfa42 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1,8 +1,8 @@
-use std::ptr;
-
+use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID};
+use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
@@ -28,6 +28,10 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Span, SyntaxContext};
use thin_vec::ThinVec;
+use crate::errors::{
+ AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive,
+ ExplicitUnsafeTraits,
+};
use crate::imports::{Import, ImportKind};
use crate::late::{PatternSource, Rib};
use crate::path_names_to_string;
@@ -97,6 +101,7 @@ pub(crate) struct ImportSuggestion {
pub descr: &'static str,
pub path: Path,
pub accessible: bool,
+ pub via_import: bool,
/// An extra note that should be issued if this item is suggested
pub note: Option<String>,
}
@@ -134,9 +139,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
let mut reported_spans = FxHashSet::default();
- for error in &self.privacy_errors {
+ for error in std::mem::take(&mut self.privacy_errors) {
if reported_spans.insert(error.dedup_span) {
- self.report_privacy_error(error);
+ self.report_privacy_error(&error);
}
}
}
@@ -175,13 +180,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- pub(crate) fn report_conflict<'b>(
+ pub(crate) fn report_conflict(
&mut self,
parent: Module<'_>,
ident: Ident,
ns: Namespace,
- new_binding: &NameBinding<'b>,
- old_binding: &NameBinding<'b>,
+ new_binding: NameBinding<'a>,
+ old_binding: NameBinding<'a>,
) {
// Error on the second of two conflicting names
if old_binding.span.lo() > new_binding.span.lo() {
@@ -255,7 +260,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// See https://github.com/rust-lang/rust/issues/32354
use NameBindingKind::Import;
- let can_suggest = |binding: &NameBinding<'_>, import: &self::Import<'_>| {
+ let can_suggest = |binding: NameBinding<'_>, import: self::Import<'_>| {
!binding.span.is_dummy()
&& !matches!(import.kind, ImportKind::MacroUse | ImportKind::MacroExport)
};
@@ -265,22 +270,22 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
(Import { import: new, .. }, Import { import: old, .. })
if {
(new.has_attributes || old.has_attributes)
- && can_suggest(old_binding, old)
- && can_suggest(new_binding, new)
+ && can_suggest(old_binding, *old)
+ && can_suggest(new_binding, *new)
} =>
{
if old.has_attributes {
- Some((new, new_binding.span, true))
+ Some((*new, new_binding.span, true))
} else {
- Some((old, old_binding.span, true))
+ Some((*old, old_binding.span, true))
}
}
// Otherwise prioritize the new binding.
- (Import { import, .. }, other) if can_suggest(new_binding, import) => {
- Some((import, new_binding.span, other.is_import()))
+ (Import { import, .. }, other) if can_suggest(new_binding, *import) => {
+ Some((*import, new_binding.span, other.is_import()))
}
- (other, Import { import, .. }) if can_suggest(old_binding, import) => {
- Some((import, old_binding.span, other.is_import()))
+ (other, Import { import, .. }) if can_suggest(old_binding, *import) => {
+ Some((*import, old_binding.span, other.is_import()))
}
_ => None,
};
@@ -334,7 +339,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&self,
err: &mut Diagnostic,
name: Symbol,
- import: &Import<'_>,
+ import: Import<'_>,
binding_span: Span,
) {
let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
@@ -374,16 +379,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
_ => unreachable!(),
}
- let rename_msg = "you can use `as` to change the binding name of the import";
if let Some(suggestion) = suggestion {
- err.span_suggestion(
- binding_span,
- rename_msg,
- suggestion,
- Applicability::MaybeIncorrect,
- );
+ err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion });
} else {
- err.span_label(binding_span, rename_msg);
+ err.subdiagnostic(ChangeImportBinding { span: binding_span });
}
}
@@ -412,7 +411,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn add_suggestion_for_duplicate_nested_use(
&self,
err: &mut Diagnostic,
- import: &Import<'_>,
+ import: Import<'_>,
binding_span: Span,
) {
assert!(import.is_nested());
@@ -454,7 +453,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&mut self,
finalize: Option<Finalize>,
path: &[Segment],
- second_binding: Option<&NameBinding<'_>>,
+ second_binding: Option<NameBinding<'_>>,
) {
let Some(Finalize { node_id, root_span, .. }) = finalize else {
return;
@@ -776,7 +775,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
.tcx
.sess
.create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span }),
- ResolutionError::FailedToResolve { label, suggestion } => {
+ ResolutionError::FailedToResolve { last_segment, label, suggestion, module } => {
let mut err =
struct_span_err!(self.tcx.sess, span, E0433, "failed to resolve: {}", &label);
err.span_label(span, label);
@@ -789,6 +788,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
err.multipart_suggestion(msg, suggestions, applicability);
}
+ if let Some(ModuleOrUniformRoot::Module(module)) = module
+ && let Some(module) = module.opt_def_id()
+ && let Some(last_segment) = last_segment
+ {
+ self.find_cfg_stripped(&mut err, &last_segment, module);
+ }
+
err
}
ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
@@ -971,9 +977,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
VisResolutionError::AncestorOnly(span) => {
self.tcx.sess.create_err(errs::AncestorOnly(span))
}
- VisResolutionError::FailedToResolve(span, label, suggestion) => {
- self.into_struct_error(span, ResolutionError::FailedToResolve { label, suggestion })
- }
+ VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error(
+ span,
+ ResolutionError::FailedToResolve {
+ last_segment: None,
+ label,
+ suggestion,
+ module: None,
+ },
+ ),
VisResolutionError::ExpectedFound(span, path_str, res) => {
self.tcx.sess.create_err(errs::ExpectedFound { span, res, path_str })
}
@@ -1184,7 +1196,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// avoid suggesting anything with a hygienic name
if ident.name == lookup_ident.name
&& ns == namespace
- && !ptr::eq(in_module, parent_scope.module)
+ && in_module != parent_scope.module
&& !ident.span.normalize_to_macros_2_0().from_expansion()
{
let res = name_binding.res();
@@ -1243,6 +1255,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
path,
accessible: child_accessible,
note,
+ via_import,
});
}
}
@@ -1337,6 +1350,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
macro_kind: MacroKind,
parent_scope: &ParentScope<'a>,
ident: Ident,
+ krate: &Crate,
) {
let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
let suggestion = self.early_lookup_typo_candidate(
@@ -1349,25 +1363,28 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let import_suggestions =
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
+ let (span, found_use) = match parent_scope.module.nearest_parent_mod().as_local() {
+ Some(def_id) => UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]),
+ None => (None, FoundUse::No),
+ };
show_candidates(
self.tcx,
err,
- None,
+ span,
&import_suggestions,
Instead::No,
- FoundUse::Yes,
+ found_use,
DiagnosticMode::Normal,
vec![],
"",
);
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);
+ err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
return;
}
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
- err.help("have you added the `#[macro_use]` on the module/import?");
+ err.subdiagnostic(AddedMacroUse);
return;
}
if ident.name == kw::Default
@@ -1376,19 +1393,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let span = self.def_span(def_id);
let source_map = self.tcx.sess.source_map();
let head_span = source_map.guess_head_span(span);
- if let Ok(head) = source_map.span_to_snippet(head_span) {
- err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect);
- } else {
- err.span_help(
- head_span,
- "consider adding `#[derive(Default)]` to this enum",
- );
- }
+ err.subdiagnostic(ConsiderAddingADerive {
+ span: head_span.shrink_to_lo(),
+ suggestion: format!("#[derive(Default)]\n")
+ });
}
for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
ident,
- ScopeSet::All(ns, false),
+ ScopeSet::All(ns),
&parent_scope,
None,
false,
@@ -1500,7 +1513,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
true
}
- fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
+ fn binding_description(&self, b: NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
let res = b.res();
if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) {
// These already contain the "built-in" prefix or look bad with it.
@@ -1540,7 +1553,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
err.span_label(ident.span, "ambiguous name");
err.note(format!("ambiguous because of {}", kind.descr()));
- let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
+ 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}");
@@ -1580,7 +1593,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
/// If the binding refers to a tuple struct constructor with fields,
/// returns the span of its fields.
- fn ctor_fields_span(&self, binding: &NameBinding<'_>) -> Option<Span> {
+ fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option<Span> {
if let NameBindingKind::Res(Res::Def(
DefKind::Ctor(CtorOf::Struct, CtorKind::Fn),
ctor_def_id,
@@ -1596,8 +1609,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None
}
- fn report_privacy_error(&self, privacy_error: &PrivacyError<'_>) {
- let PrivacyError { ident, binding, .. } = *privacy_error;
+ fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) {
+ let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } =
+ *privacy_error;
let res = binding.res();
let ctor_fields_span = self.ctor_fields_span(binding);
@@ -1606,7 +1620,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr };
let import_descr = nonimport_descr.clone() + " import";
let get_descr =
- |b: &NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
+ |b: NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
// Print the primary message.
let descr = get_descr(binding);
@@ -1614,6 +1628,33 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
struct_span_err!(self.tcx.sess, ident.span, E0603, "{} `{}` is private", descr, ident);
err.span_label(ident.span, format!("private {}", descr));
+ if let Some((this_res, outer_ident)) = outermost_res {
+ let import_suggestions = self.lookup_import_candidates(
+ outer_ident,
+ this_res.ns().unwrap_or(Namespace::TypeNS),
+ &parent_scope,
+ &|res: Res| res == this_res,
+ );
+ let point_to_def = !show_candidates(
+ self.tcx,
+ &mut err,
+ Some(dedup_span.until(outer_ident.span.shrink_to_hi())),
+ &import_suggestions,
+ Instead::Yes,
+ FoundUse::Yes,
+ DiagnosticMode::Import,
+ vec![],
+ "",
+ );
+ // If we suggest importing a public re-export, don't point at the definition.
+ if point_to_def && ident.span != outer_ident.span {
+ err.span_label(
+ outer_ident.span,
+ format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()),
+ );
+ }
+ }
+
let mut non_exhaustive = None;
// If an ADT is foreign and marked as `non_exhaustive`, then that's
// probably why we have the privacy error.
@@ -1659,7 +1700,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
_ => None,
};
- let first = ptr::eq(binding, first_binding);
+ let first = binding == first_binding;
let msg = format!(
"{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
and_refers_to = if first { "" } else { "...and refers to " },
@@ -1689,7 +1730,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
pub(crate) fn find_similarly_named_module_or_crate(
&mut self,
ident: Symbol,
- current_module: &Module<'a>,
+ current_module: Module<'a>,
) -> Option<Symbol> {
let mut candidates = self
.extern_prelude
@@ -1699,7 +1740,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
self.module_map
.iter()
.filter(|(_, module)| {
- current_module.is_ancestor_of(module) && !ptr::eq(current_module, *module)
+ current_module.is_ancestor_of(**module) && current_module != **module
})
.flat_map(|(_, module)| module.kind.name()),
)
@@ -1719,12 +1760,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- ignore_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<NameBinding<'a>>,
module: Option<ModuleOrUniformRoot<'a>>,
- i: usize,
+ failed_segment_idx: usize,
ident: Ident,
) -> (String, Option<Suggestion>) {
- let is_last = i == path.len() - 1;
+ let is_last = failed_segment_idx == path.len() - 1;
let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
let module_res = match module {
Some(ModuleOrUniformRoot::Module(module)) => module.res(),
@@ -1758,8 +1799,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
} else {
(format!("could not find `{ident}` in the crate root"), None)
}
- } else if i > 0 {
- let parent = path[i - 1].ident.name;
+ } else if failed_segment_idx > 0 {
+ let parent = path[failed_segment_idx - 1].ident.name;
let parent = match parent {
// ::foo is mounted at the crate root for 2015, and is the extern
// prelude for 2018+
@@ -1798,10 +1839,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
_ => None,
}
} else {
- let scopes = ScopeSet::All(ns_to_try, opt_ns.is_none());
self.early_resolve_ident_in_lexical_scope(
ident,
- scopes,
+ ScopeSet::All(ns_to_try),
parent_scope,
None,
false,
@@ -1903,7 +1943,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
suggestion = suggestion.or_else(|| {
- self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
+ self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map(
|sugg| {
(
vec![(ident.span, sugg.to_string())],
@@ -2072,7 +2112,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
/// ```
pub(crate) fn check_for_module_export_macro(
&mut self,
- import: &'a Import<'a>,
+ import: Import<'a>,
module: ModuleOrUniformRoot<'a>,
ident: Ident,
) -> Option<(Option<Suggestion>, Option<String>)> {
@@ -2084,9 +2124,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
crate_module = parent;
}
- if ModuleOrUniformRoot::same_def(ModuleOrUniformRoot::Module(crate_module), module) {
- // Don't make a suggestion if the import was already from the root of the
- // crate.
+ if module == ModuleOrUniformRoot::Module(crate_module) {
+ // Don't make a suggestion if the import was already from the root of the crate.
return None;
}
@@ -2207,6 +2246,44 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None
}
}
+
+ /// Finds a cfg-ed out item inside `module` with the matching name.
+ pub(crate) fn find_cfg_stripped(
+ &mut self,
+ err: &mut Diagnostic,
+ last_segment: &Symbol,
+ module: DefId,
+ ) {
+ let local_items;
+ let symbols = if module.is_local() {
+ local_items = self
+ .stripped_cfg_items
+ .iter()
+ .filter_map(|item| {
+ let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id();
+ Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() })
+ })
+ .collect::<Vec<_>>();
+ local_items.as_slice()
+ } else {
+ self.tcx.stripped_cfg_items(module.krate)
+ };
+
+ for &StrippedCfgItem { parent_module, name, ref cfg } in symbols {
+ if parent_module != module || name.name != *last_segment {
+ continue;
+ }
+
+ err.span_note(name.span, "found an item that was configured out");
+
+ if let MetaItemKind::List(nested) = &cfg.kind
+ && let NestedMetaItem::MetaItem(meta_item) = &nested[0]
+ && let MetaItemKind::NameValue(feature_name) = &meta_item.kind
+ {
+ err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol));
+ }
+ }
+ }
}
/// Given a `binding_span` of a binding within a use statement:
@@ -2404,7 +2481,8 @@ pub(crate) fn import_candidates(
/// When an entity with a given name is not available in scope, we search for
/// entities with that name in all crates. This method allows outputting the
-/// results of this search in a programmer-friendly way
+/// results of this search in a programmer-friendly way. If any entities are
+/// found and suggested, returns `true`, otherwise returns `false`.
fn show_candidates(
tcx: TyCtxt<'_>,
err: &mut Diagnostic,
@@ -2416,19 +2494,19 @@ fn show_candidates(
mode: DiagnosticMode,
path: Vec<Segment>,
append: &str,
-) {
+) -> bool {
if candidates.is_empty() {
- return;
+ return false;
}
- let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
+ let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
Vec::new();
- let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>)> =
+ let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
Vec::new();
candidates.iter().for_each(|c| {
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
- .push((path_names_to_string(&c.path), c.descr, c.did, &c.note))
+ .push((path_names_to_string(&c.path), c.descr, c.did, &c.note, c.via_import))
});
// we want consistent results across executions, but candidates are produced
@@ -2436,26 +2514,31 @@ fn show_candidates(
for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
path_strings.sort_by(|a, b| a.0.cmp(&b.0));
let core_path_strings =
- path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
+ path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
path_strings.extend(core_path_strings);
path_strings.dedup_by(|a, b| a.0 == b.0);
}
if !accessible_path_strings.is_empty() {
- let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
- ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
- } else {
- ("one of these", "items", String::new())
- };
+ let (determiner, kind, name, through) =
+ if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] {
+ (
+ "this",
+ *descr,
+ format!(" `{name}`"),
+ if *via_import { " through its public re-export" } else { "" },
+ )
+ } else {
+ ("one of these", "items", String::new(), "")
+ };
let instead = if let Instead::Yes = instead { " instead" } else { "" };
let mut msg = if let DiagnosticMode::Pattern = mode {
format!(
- "if you meant to match on {}{}{}, use the full path in the pattern",
- kind, instead, name
+ "if you meant to match on {kind}{instead}{name}, use the full path in the pattern",
)
} else {
- format!("consider importing {} {}{}", determiner, kind, instead)
+ format!("consider importing {determiner} {kind}{through}{instead}")
};
for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
@@ -2471,7 +2554,7 @@ fn show_candidates(
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
);
- return;
+ return true;
}
DiagnosticMode::Import => ("", ""),
DiagnosticMode::Normal => ("use ", ";\n"),
@@ -2512,6 +2595,7 @@ fn show_candidates(
err.help(msg);
}
+ true
} else if !matches!(mode, DiagnosticMode::Import) {
assert!(!inaccessible_path_strings.is_empty());
@@ -2520,13 +2604,9 @@ fn show_candidates(
} else {
""
};
- if inaccessible_path_strings.len() == 1 {
- let (name, descr, def_id, note) = &inaccessible_path_strings[0];
+ if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] {
let msg = format!(
- "{}{} `{}`{} exists but is inaccessible",
- prefix,
- descr,
- name,
+ "{prefix}{descr} `{name}`{} exists but is inaccessible",
if let DiagnosticMode::Pattern = mode { ", which" } else { "" }
);
@@ -2540,14 +2620,14 @@ fn show_candidates(
err.note(msg);
}
if let Some(note) = (*note).as_deref() {
- err.note(note);
+ err.note(note.to_string());
}
} else {
- let (_, descr_first, _, _) = &inaccessible_path_strings[0];
+ let (_, descr_first, _, _, _) = &inaccessible_path_strings[0];
let descr = if inaccessible_path_strings
.iter()
.skip(1)
- .all(|(_, descr, _, _)| descr == descr_first)
+ .all(|(_, descr, _, _, _)| descr == descr_first)
{
descr_first
} else {
@@ -2560,7 +2640,7 @@ fn show_candidates(
let mut has_colon = false;
let mut spans = Vec::new();
- for (name, _, def_id, _) in &inaccessible_path_strings {
+ for (name, _, def_id, _, _) in &inaccessible_path_strings {
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
let span = tcx.source_span(local_def_id);
let span = tcx.sess.source_map().guess_head_span(span);
@@ -2586,6 +2666,9 @@ fn show_candidates(
err.span_note(multi_span, msg);
}
+ true
+ } else {
+ false
}
}
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index 7393bdb38..eb210532f 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -5,7 +5,6 @@ use rustc_ast::visit::Visitor;
use rustc_ast::Crate;
use rustc_ast::EnumDef;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::intern::Interned;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_middle::middle::privacy::Level;
@@ -13,12 +12,10 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility};
use rustc_middle::ty::Visibility;
use std::mem;
-type ImportId<'a> = Interned<'a, NameBinding<'a>>;
-
#[derive(Clone, Copy)]
enum ParentId<'a> {
Def(LocalDefId),
- Import(ImportId<'a>),
+ Import(NameBinding<'a>),
}
impl ParentId<'_> {
@@ -36,7 +33,7 @@ pub(crate) struct EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
/// While walking import chains we need to track effective visibilities per-binding, and def id
/// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
/// bindings can correspond to a single def id in imports. So we keep a separate table.
- import_effective_visibilities: EffectiveVisibilities<ImportId<'a>>,
+ import_effective_visibilities: EffectiveVisibilities<NameBinding<'a>>,
// It's possible to recalculate this at any point, but it's relatively expensive.
current_private_vis: Visibility,
changed: bool,
@@ -47,7 +44,7 @@ impl Resolver<'_, '_> {
self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
}
- fn private_vis_import(&mut self, binding: ImportId<'_>) -> Visibility {
+ fn private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility {
let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
Visibility::Restricted(
import
@@ -75,13 +72,13 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
pub(crate) fn compute_effective_visibilities<'c>(
r: &'r mut Resolver<'a, 'tcx>,
krate: &'c Crate,
- ) -> FxHashSet<Interned<'a, NameBinding<'a>>> {
+ ) -> FxHashSet<NameBinding<'a>> {
let mut visitor = EffectiveVisibilitiesVisitor {
r,
def_effective_visibilities: Default::default(),
import_effective_visibilities: Default::default(),
current_private_vis: Visibility::Restricted(CRATE_DEF_ID),
- changed: false,
+ changed: true,
};
visitor.def_effective_visibilities.update_root();
@@ -133,8 +130,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
// lint. For all bindings added to the table this way `is_ambiguity` returns true.
let mut parent_id = ParentId::Def(module_id);
while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind {
- let binding_id = ImportId::new_unchecked(binding);
- self.update_import(binding_id, parent_id);
+ self.update_import(binding, parent_id);
if binding.ambiguity.is_some() {
// Stop at the root ambiguity, further bindings in the chain should not
@@ -143,7 +139,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
break;
}
- parent_id = ParentId::Import(binding_id);
+ parent_id = ParentId::Import(binding);
binding = nested_binding;
}
@@ -192,7 +188,7 @@ impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
}
}
- fn update_import(&mut self, binding: ImportId<'a>, parent_id: ParentId<'a>) {
+ fn update_import(&mut self, binding: NameBinding<'a>, parent_id: ParentId<'a>) {
let nominal_vis = binding.vis.expect_local();
let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
let inherited_eff_vis = self.effective_vis_or_private(parent_id);
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 2ab55f126..e4b89c658 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -330,6 +330,7 @@ pub(crate) struct ParamInTyOfConstParam {
pub(crate) param_kind: Option<ParamKindInTyOfConstParam>,
}
+#[derive(Debug)]
#[derive(Subdiagnostic)]
pub(crate) enum ParamKindInTyOfConstParam {
#[note(resolve_type_param_in_ty_of_const_param)]
@@ -365,6 +366,7 @@ pub(crate) struct ParamInNonTrivialAnonConst {
#[help(resolve_param_in_non_trivial_anon_const_help)]
pub(crate) struct ParamInNonTrivialAnonConstHelp;
+#[derive(Debug)]
#[derive(Subdiagnostic)]
pub(crate) enum ParamKindInNonTrivialAnonConst {
#[note(resolve_type_param_in_non_trivial_anon_const)]
@@ -562,6 +564,7 @@ pub(crate) struct CfgAccessibleUnsure {
pub(crate) span: Span,
}
+#[derive(Debug)]
#[derive(Diagnostic)]
#[diag(resolve_param_in_enum_discriminant)]
pub(crate) struct ParamInEnumDiscriminant {
@@ -573,6 +576,7 @@ pub(crate) struct ParamInEnumDiscriminant {
pub(crate) param_kind: ParamKindInEnumDiscriminant,
}
+#[derive(Debug)]
#[derive(Subdiagnostic)]
pub(crate) enum ParamKindInEnumDiscriminant {
#[note(resolve_type_param_in_enum_discriminant)]
@@ -582,3 +586,144 @@ pub(crate) enum ParamKindInEnumDiscriminant {
#[note(resolve_lifetime_param_in_enum_discriminant)]
Lifetime,
}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_change_import_binding)]
+pub(crate) struct ChangeImportBinding {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+ resolve_change_import_binding,
+ code = "{suggestion}",
+ applicability = "maybe-incorrect"
+)]
+pub(crate) struct ChangeImportBindingSuggestion {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) suggestion: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_imports_cannot_refer_to)]
+pub(crate) struct ImportsCannotReferTo<'a> {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) what: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_find_ident_in_this_scope)]
+pub(crate) struct CannotFindIdentInThisScope<'a> {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) expected: &'a str,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_explicit_unsafe_traits)]
+pub(crate) struct ExplicitUnsafeTraits {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_added_macro_use)]
+pub(crate) struct AddedMacroUse;
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+ resolve_consider_adding_a_derive,
+ code = "{suggestion}",
+ applicability = "maybe-incorrect"
+)]
+pub(crate) struct ConsiderAddingADerive {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) suggestion: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_determine_import_resolution)]
+pub(crate) struct CannotDetermineImportResolution {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_private, code = "E0364")]
+pub(crate) struct CannotBeReexportedPrivate {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_crate_public, code = "E0364")]
+pub(crate) struct CannotBeReexportedCratePublic {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_private, code = "E0365")]
+#[note(resolve_consider_declaring_with_pub)]
+pub(crate) struct CannotBeReexportedPrivateNS {
+ #[primary_span]
+ #[label(resolve_reexport_of_private)]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_crate_public, code = "E0365")]
+#[note(resolve_consider_declaring_with_pub)]
+pub(crate) struct CannotBeReexportedCratePublicNS {
+ #[primary_span]
+ #[label(resolve_reexport_of_crate_public)]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_consider_adding_macro_export)]
+pub(crate) struct ConsiderAddingMacroExport {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_consider_marking_as_pub)]
+pub(crate) struct ConsiderMarkingAsPub {
+ #[primary_span]
+ pub(crate) span: Span,
+ pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_glob_import_possible_crates)]
+pub(crate) struct CannotGlobImportAllCrates {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_items_in_traits_are_not_importable)]
+pub(crate) struct ItemsInTraitsAreNotImportable {
+ #[primary_span]
+ pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_is_not_directly_importable, code = "E0253")]
+pub(crate) struct IsNotDirectlyImportable {
+ #[primary_span]
+ #[label]
+ pub(crate) span: Span,
+ pub(crate) target: Ident,
+}
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 945c7ce3a..520fab1f0 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -11,8 +11,6 @@ use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContex
use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, DUMMY_SP};
-use std::ptr;
-
use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
use crate::late::{
ConstantHasGenerics, HasGenericParams, NoConstantGenericsReason, PathSource, Rib, RibKind,
@@ -20,7 +18,7 @@ use crate::late::{
use crate::macros::{sub_namespace_match, MacroRulesScope};
use crate::BindingKey;
use crate::{errors, AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize};
-use crate::{Import, ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res};
use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak};
@@ -88,7 +86,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let rust_2015 = ctxt.edition().is_rust_2015();
let (ns, macro_kind, is_absolute_path) = match scope_set {
- ScopeSet::All(ns, _) => (ns, None, false),
+ ScopeSet::All(ns) => (ns, None, false),
ScopeSet::AbsolutePath(ns) => (ns, None, true),
ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
ScopeSet::Late(ns, ..) => (ns, None, false),
@@ -284,7 +282,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
ribs: &[Rib<'a>],
- ignore_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
assert!(ns == TypeNS || ns == ValueNS);
let orig_ident = ident;
@@ -370,7 +368,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
/// expansion and import resolution (perhaps they can be merged in the future).
/// The function is used for resolving initial segments of macro paths (e.g., `foo` in
/// `foo::bar!();` or `foo!();`) and also for import paths on 2018 edition.
- #[instrument(level = "debug", skip(self, scope_set))]
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn early_resolve_ident_in_lexical_scope(
&mut self,
orig_ident: Ident,
@@ -378,8 +376,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
force: bool,
- ignore_binding: Option<&'a NameBinding<'a>>,
- ) -> Result<&'a NameBinding<'a>, Determinacy> {
+ ignore_binding: Option<NameBinding<'a>>,
+ ) -> Result<NameBinding<'a>, Determinacy> {
bitflags::bitflags! {
struct Flags: u8 {
const MACRO_RULES = 1 << 0;
@@ -397,11 +395,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
return Err(Determinacy::Determined);
}
- let (ns, macro_kind, is_import) = match scope_set {
- ScopeSet::All(ns, is_import) => (ns, None, is_import),
- ScopeSet::AbsolutePath(ns) => (ns, None, false),
- ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
- ScopeSet::Late(ns, ..) => (ns, None, false),
+ let (ns, macro_kind) = match scope_set {
+ ScopeSet::All(ns) => (ns, None),
+ ScopeSet::AbsolutePath(ns) => (ns, None),
+ ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
+ ScopeSet::Late(ns, ..) => (ns, None),
};
// This is *the* result, resolution from the scope closest to the resolved identifier.
@@ -415,7 +413,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// }
// So we have to save the innermost solution and continue searching in outer scopes
// to detect potential ambiguities.
- let mut innermost_result: Option<(&NameBinding<'_>, Flags)> = None;
+ let mut innermost_result: Option<(NameBinding<'_>, Flags)> = None;
let mut determinacy = Determinacy::Determined;
// Go through all the scopes and try to resolve the name.
@@ -538,7 +536,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
),
);
}
- let misc_flags = if ptr::eq(module, this.graph_root) {
+ let misc_flags = if module == this.graph_root {
Flags::MISC_SUGGEST_CRATE
} else if module.is_normal() {
Flags::MISC_SUGGEST_SELF
@@ -631,9 +629,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let derive_helper_compat =
Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat);
- let ambiguity_error_kind = if is_import {
- Some(AmbiguityKind::Import)
- } else if is_builtin(innermost_res) || is_builtin(res) {
+ let ambiguity_error_kind = if is_builtin(innermost_res)
+ || is_builtin(res)
+ {
Some(AmbiguityKind::BuiltinAttr)
} else if innermost_res == derive_helper_compat
|| res == derive_helper_compat && innermost_res != derive_helper
@@ -717,7 +715,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- ) -> Result<&'a NameBinding<'a>, Determinacy> {
+ ) -> Result<NameBinding<'a>, Determinacy> {
self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
.map_err(|(determinacy, _)| determinacy)
}
@@ -730,8 +728,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ns: Namespace,
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
- ignore_binding: Option<&'a NameBinding<'a>>,
- ) -> Result<&'a NameBinding<'a>, Determinacy> {
+ ignore_binding: Option<NameBinding<'a>>,
+ ) -> Result<NameBinding<'a>, Determinacy> {
self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
.map_err(|(determinacy, _)| determinacy)
}
@@ -744,8 +742,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ns: Namespace,
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
- ignore_binding: Option<&'a NameBinding<'a>>,
- ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
+ ignore_binding: Option<NameBinding<'a>>,
+ ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
let tmp_parent_scope;
let mut adjusted_parent_scope = parent_scope;
match module {
@@ -782,8 +780,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ns: Namespace,
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
- ignore_binding: Option<&'a NameBinding<'a>>,
- ) -> Result<&'a NameBinding<'a>, Determinacy> {
+ ignore_binding: Option<NameBinding<'a>>,
+ ) -> Result<NameBinding<'a>, Determinacy> {
self.resolve_ident_in_module_unadjusted_ext(
module,
ident,
@@ -809,8 +807,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
finalize: Option<Finalize>,
// This binding should be ignored during in-module resolution, so that we don't get
// "self-confirming" import resolutions during import validation and checking.
- ignore_binding: Option<&'a NameBinding<'a>>,
- ) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
+ ignore_binding: Option<NameBinding<'a>>,
+ ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
let module = match module {
ModuleOrUniformRoot::Module(module) => module,
ModuleOrUniformRoot::CrateRootAndExternPrelude => {
@@ -853,10 +851,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- let scopes = ScopeSet::All(ns, true);
let binding = self.early_resolve_ident_in_lexical_scope(
ident,
- scopes,
+ ScopeSet::All(ns),
parent_scope,
finalize,
finalize.is_some(),
@@ -874,13 +871,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// binding if it exists. What we really want here is having two separate scopes in
// a module - one for non-globs and one for globs, but until that's done use this
// hack to avoid inconsistent resolution ICEs during import validation.
- let binding =
- [resolution.binding, resolution.shadowed_glob].into_iter().find_map(|binding| {
- match (binding, ignore_binding) {
- (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
- _ => binding,
- }
- });
+ let binding = [resolution.binding, resolution.shadowed_glob]
+ .into_iter()
+ .find_map(|binding| if binding == ignore_binding { None } else { binding });
if let Some(Finalize { path_span, report_private, .. }) = finalize {
let Some(binding) = binding else {
@@ -893,6 +886,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
ident,
binding,
dedup_span: path_span,
+ outermost_res: None,
+ parent_scope: *parent_scope,
});
} else {
return Err((Determined, Weak::No));
@@ -916,11 +911,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT {
- if let NameBindingKind::Import {
- import: Import { kind: ImportKind::MacroExport, .. },
- ..
- } = binding.kind
- {
+ if let NameBindingKind::Import { import, .. } = binding.kind
+ && matches!(import.kind, ImportKind::MacroExport) {
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
}
}
@@ -929,7 +921,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
return Ok(binding);
}
- let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
+ let check_usable = |this: &mut Self, binding: NameBinding<'a>| {
let usable = this.is_accessible_from(binding.vis, parent_scope.module);
if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
};
@@ -954,7 +946,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
if let Some(ignored) = ignore_binding &&
let NameBindingKind::Import { import, .. } = ignored.kind &&
- ptr::eq(import, &**single_import) {
+ import == *single_import {
// Ignore not just the binding itself, but if it has a shadowed_glob,
// ignore that, too, because this loop is supposed to only process
// named imports.
@@ -1351,7 +1343,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
- ignore_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<NameBinding<'a>>,
) -> PathResult<'a> {
self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
}
@@ -1363,22 +1355,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
parent_scope: &ParentScope<'a>,
finalize: Option<Finalize>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- ignore_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<NameBinding<'a>>,
) -> PathResult<'a> {
- debug!(
- "resolve_path(path={:?}, opt_ns={:?}, finalize={:?}) path_len: {}",
- path,
- opt_ns,
- finalize,
- path.len()
- );
-
let mut module = None;
let mut allow_super = true;
let mut second_binding = None;
- for (i, &Segment { ident, id, .. }) in path.iter().enumerate() {
- debug!("resolve_path ident {} {:?} {:?}", i, ident, id);
+ // We'll provide more context to the privacy errors later, up to `len`.
+ let privacy_errors_len = self.privacy_errors.len();
+
+ for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() {
+ debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id);
let record_segment_res = |this: &mut Self, res| {
if finalize.is_some() {
if let Some(id) = id {
@@ -1390,7 +1377,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
};
- let is_last = i + 1 == path.len();
+ let is_last = segment_idx + 1 == path.len();
let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
let name = ident.name;
@@ -1399,7 +1386,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if ns == TypeNS {
if allow_super && name == kw::Super {
let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0();
- let self_module = match i {
+ let self_module = match segment_idx {
0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)),
_ => match module {
Some(ModuleOrUniformRoot::Module(module)) => Some(module),
@@ -1414,11 +1401,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
continue;
}
}
- return PathResult::failed(ident.span, false, finalize.is_some(), || {
- ("there are too many leading `super` keywords".to_string(), None)
- });
+ return PathResult::failed(
+ ident.span,
+ false,
+ finalize.is_some(),
+ module,
+ || ("there are too many leading `super` keywords".to_string(), None),
+ );
}
- if i == 0 {
+ if segment_idx == 0 {
if name == kw::SelfLower {
let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0();
module = Some(ModuleOrUniformRoot::Module(
@@ -1447,14 +1438,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
// Report special messages for path segment keywords in wrong positions.
- if ident.is_path_segment_keyword() && i != 0 {
- return PathResult::failed(ident.span, false, finalize.is_some(), || {
+ if ident.is_path_segment_keyword() && segment_idx != 0 {
+ return PathResult::failed(ident.span, false, finalize.is_some(), module, || {
let name_str = if name == kw::PathRoot {
"crate root".to_string()
} else {
format!("`{}`", name)
};
- let label = if i == 1 && path[0].ident.name == kw::PathRoot {
+ let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot {
format!("global paths cannot start with {}", name_str)
} else {
format!("{} in paths can only be used in start position", name_str)
@@ -1463,66 +1454,61 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
});
}
- enum FindBindingResult<'a> {
- Binding(Result<&'a NameBinding<'a>, Determinacy>),
- Res(Res),
- }
- let find_binding_in_ns = |this: &mut Self, ns| {
- let binding = if let Some(module) = module {
- this.resolve_ident_in_module(
- module,
- ident,
- ns,
- parent_scope,
- finalize,
- ignore_binding,
- )
- } else if let Some(ribs) = ribs
- && let Some(TypeNS | ValueNS) = opt_ns
- {
- match this.resolve_ident_in_lexical_scope(
- ident,
- ns,
- parent_scope,
- finalize,
- &ribs[ns],
- ignore_binding,
- ) {
- // we found a locally-imported or available item/module
- Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
- // we found a local variable or type param
- Some(LexicalScopeBinding::Res(res)) => return FindBindingResult::Res(res),
- _ => Err(Determinacy::determined(finalize.is_some())),
+ let binding = if let Some(module) = module {
+ self.resolve_ident_in_module(
+ module,
+ ident,
+ ns,
+ parent_scope,
+ finalize,
+ ignore_binding,
+ )
+ } else if let Some(ribs) = ribs && let Some(TypeNS | ValueNS) = opt_ns {
+ match self.resolve_ident_in_lexical_scope(
+ ident,
+ ns,
+ parent_scope,
+ finalize,
+ &ribs[ns],
+ ignore_binding,
+ ) {
+ // we found a locally-imported or available item/module
+ Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
+ // we found a local variable or type param
+ Some(LexicalScopeBinding::Res(res)) => {
+ record_segment_res(self, res);
+ return PathResult::NonModule(PartialRes::with_unresolved_segments(
+ res,
+ path.len() - 1,
+ ));
}
- } else {
- let scopes = ScopeSet::All(ns, opt_ns.is_none());
- this.early_resolve_ident_in_lexical_scope(
- ident,
- scopes,
- parent_scope,
- finalize,
- finalize.is_some(),
- ignore_binding,
- )
- };
- FindBindingResult::Binding(binding)
- };
- let binding = match find_binding_in_ns(self, ns) {
- FindBindingResult::Res(res) => {
- record_segment_res(self, res);
- return PathResult::NonModule(PartialRes::with_unresolved_segments(
- res,
- path.len() - 1,
- ));
+ _ => Err(Determinacy::determined(finalize.is_some())),
}
- FindBindingResult::Binding(binding) => binding,
+ } else {
+ self.early_resolve_ident_in_lexical_scope(
+ ident,
+ ScopeSet::All(ns),
+ parent_scope,
+ finalize,
+ finalize.is_some(),
+ ignore_binding,
+ )
};
+
match binding {
Ok(binding) => {
- if i == 1 {
+ if segment_idx == 1 {
second_binding = Some(binding);
}
let res = binding.res();
+
+ // Mark every privacy error in this path with the res to the last element. This allows us
+ // to detect the item the user cares about and either find an alternative import, or tell
+ // the user it is not accessible.
+ for error in &mut self.privacy_errors[privacy_errors_len..] {
+ error.outermost_res = Some((res, ident));
+ }
+
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
if let Some(next_module) = binding.module() {
module = Some(ModuleOrUniformRoot::Module(next_module));
@@ -1543,17 +1529,23 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
record_segment_res(self, res);
return PathResult::NonModule(PartialRes::with_unresolved_segments(
res,
- path.len() - i - 1,
+ path.len() - segment_idx - 1,
));
} else {
- return PathResult::failed(ident.span, is_last, finalize.is_some(), || {
- let label = format!(
- "`{ident}` is {} {}, not a module",
- res.article(),
- res.descr()
- );
- (label, None)
- });
+ return PathResult::failed(
+ ident.span,
+ is_last,
+ finalize.is_some(),
+ module,
+ || {
+ let label = format!(
+ "`{ident}` is {} {}, not a module",
+ res.article(),
+ res.descr()
+ );
+ (label, None)
+ },
+ );
}
}
Err(Undetermined) => return PathResult::Indeterminate,
@@ -1562,23 +1554,29 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if opt_ns.is_some() && !module.is_normal() {
return PathResult::NonModule(PartialRes::with_unresolved_segments(
module.res().unwrap(),
- path.len() - i,
+ path.len() - segment_idx,
));
}
}
- return PathResult::failed(ident.span, is_last, finalize.is_some(), || {
- self.report_path_resolution_error(
- path,
- opt_ns,
- parent_scope,
- ribs,
- ignore_binding,
- module,
- i,
- ident,
- )
- });
+ return PathResult::failed(
+ ident.span,
+ is_last,
+ finalize.is_some(),
+ module,
+ || {
+ self.report_path_resolution_error(
+ path,
+ opt_ns,
+ parent_scope,
+ ribs,
+ ignore_binding,
+ module,
+ segment_idx,
+ ident,
+ )
+ },
+ );
}
}
}
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 7c4c05d4b..d37fe783b 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1,15 +1,18 @@
//! A bunch of methods and structures more or less related to resolving imports.
use crate::diagnostics::{import_candidates, DiagnosticMode, Suggestion};
+use crate::errors::{
+ CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
+ CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates,
+ ConsiderAddingMacroExport, ConsiderMarkingAsPub, IsNotDirectlyImportable,
+ ItemsInTraitsAreNotImportable,
+};
use crate::Determinacy::{self, *};
-use crate::Namespace::*;
+use crate::{fluent_generated as fluent, Namespace::*};
use crate::{module_to_string, names_to_string, ImportSuggestion};
-use crate::{
- AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, ModuleKind, ResolutionError,
- Resolver, Segment,
-};
+use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
-use crate::{NameBinding, NameBindingKind, PathResult};
+use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult};
use rustc_ast::NodeId;
use rustc_data_structures::fx::FxHashSet;
@@ -21,7 +24,8 @@ use rustc_middle::metadata::Reexport;
use rustc_middle::span_bug;
use rustc_middle::ty;
use rustc_session::lint::builtin::{
- AMBIGUOUS_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS,
+ AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+ UNUSED_IMPORTS,
};
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::edit_distance::find_best_match_for_name;
@@ -31,7 +35,7 @@ use rustc_span::Span;
use smallvec::SmallVec;
use std::cell::Cell;
-use std::{mem, ptr};
+use std::mem;
type Res = def::Res<NodeId>;
@@ -44,9 +48,9 @@ pub(crate) enum ImportKind<'a> {
/// `target` in `use prefix::source as target`.
target: Ident,
/// Bindings to which `source` refers to.
- source_bindings: PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>,
+ source_bindings: PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
/// Bindings introduced by `target`.
- target_bindings: PerNS<Cell<Option<&'a NameBinding<'a>>>>,
+ target_bindings: PerNS<Cell<Option<NameBinding<'a>>>>,
/// `true` for `...::{self [as target]}` imports, `false` otherwise.
type_ns_only: bool,
/// Did this import result from a nested import? ie. `use foo::{bar, baz};`
@@ -131,7 +135,7 @@ impl<'a> std::fmt::Debug for ImportKind<'a> {
/// One import.
#[derive(Debug, Clone)]
-pub(crate) struct Import<'a> {
+pub(crate) struct ImportData<'a> {
pub kind: ImportKind<'a>,
/// Node ID of the "root" use item -- this is always the same as `ImportKind`'s `id`
@@ -168,7 +172,11 @@ pub(crate) struct Import<'a> {
pub used: Cell<bool>,
}
-impl<'a> Import<'a> {
+/// All imports are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+pub(crate) type Import<'a> = Interned<'a, ImportData<'a>>;
+
+impl<'a> ImportData<'a> {
pub(crate) fn is_glob(&self) -> bool {
matches!(self.kind, ImportKind::Glob { .. })
}
@@ -210,15 +218,15 @@ impl<'a> Import<'a> {
pub(crate) struct NameResolution<'a> {
/// Single imports that may define the name in the namespace.
/// Imports are arena-allocated, so it's ok to use pointers as keys.
- pub single_imports: FxHashSet<Interned<'a, Import<'a>>>,
+ pub single_imports: FxHashSet<Import<'a>>,
/// The least shadowable known binding for this name, or None if there are no known bindings.
- pub binding: Option<&'a NameBinding<'a>>,
- pub shadowed_glob: Option<&'a NameBinding<'a>>,
+ pub binding: Option<NameBinding<'a>>,
+ pub shadowed_glob: Option<NameBinding<'a>>,
}
impl<'a> NameResolution<'a> {
/// Returns the binding for the name if it is known or None if it not known.
- pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> {
+ pub(crate) fn binding(&self) -> Option<NameBinding<'a>> {
self.binding.and_then(|binding| {
if !binding.is_glob_import() || self.single_imports.is_empty() {
Some(binding)
@@ -227,10 +235,6 @@ impl<'a> NameResolution<'a> {
}
})
}
-
- pub(crate) fn add_single_import(&mut self, import: &'a Import<'a>) {
- self.single_imports.insert(Interned::new_unchecked(import));
- }
}
/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
@@ -246,15 +250,12 @@ struct UnresolvedImportError {
// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;`
// are permitted for backward-compatibility under a deprecation lint.
-fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBinding<'_>) -> bool {
+fn pub_use_of_private_extern_crate_hack(import: Import<'_>, binding: NameBinding<'_>) -> bool {
match (&import.kind, &binding.kind) {
- (
- ImportKind::Single { .. },
- NameBindingKind::Import {
- import: Import { kind: ImportKind::ExternCrate { .. }, .. },
- ..
- },
- ) => import.expect_vis().is_public(),
+ (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) => {
+ matches!(binding_import.kind, ImportKind::ExternCrate { .. })
+ && import.expect_vis().is_public()
+ }
_ => false,
}
}
@@ -262,11 +263,7 @@ fn pub_use_of_private_extern_crate_hack(import: &Import<'_>, binding: &NameBindi
impl<'a, 'tcx> Resolver<'a, 'tcx> {
/// Given a binding and an import that resolves to it,
/// return the corresponding binding defined by the import.
- pub(crate) fn import(
- &self,
- binding: &'a NameBinding<'a>,
- import: &'a Import<'a>,
- ) -> &'a NameBinding<'a> {
+ pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> {
let import_vis = import.expect_vis().to_def_id();
let vis = if binding.vis.is_at_least(import_vis, self.tcx)
|| pub_use_of_private_extern_crate_hack(import, binding)
@@ -284,7 +281,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- self.arenas.alloc_name_binding(NameBinding {
+ self.arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Import { binding, import, used: Cell::new(false) },
ambiguity: None,
span: import.span,
@@ -298,8 +295,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&mut self,
module: Module<'a>,
key: BindingKey,
- binding: &'a NameBinding<'a>,
- ) -> Result<(), &'a NameBinding<'a>> {
+ binding: NameBinding<'a>,
+ ) -> Result<(), NameBinding<'a>> {
let res = binding.res();
self.check_reserved_macro_name(key.ident, res);
self.set_binding_parent_module(binding, module);
@@ -337,7 +334,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
} else {
resolution.binding = Some(nonglob_binding);
}
- resolution.shadowed_glob = Some(glob_binding);
+
+ if let Some(old_binding) = resolution.shadowed_glob {
+ assert!(old_binding.is_glob_import());
+ if glob_binding.res() != old_binding.res() {
+ resolution.shadowed_glob = Some(this.ambiguity(
+ AmbiguityKind::GlobVsGlob,
+ old_binding,
+ glob_binding,
+ ));
+ } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+ resolution.shadowed_glob = Some(glob_binding);
+ }
+ } else {
+ resolution.shadowed_glob = Some(glob_binding);
+ }
}
(false, false) => {
return Err(old_binding);
@@ -354,12 +365,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn ambiguity(
&self,
kind: AmbiguityKind,
- primary_binding: &'a NameBinding<'a>,
- secondary_binding: &'a NameBinding<'a>,
- ) -> &'a NameBinding<'a> {
- self.arenas.alloc_name_binding(NameBinding {
+ primary_binding: NameBinding<'a>,
+ secondary_binding: NameBinding<'a>,
+ ) -> NameBinding<'a> {
+ self.arenas.alloc_name_binding(NameBindingData {
ambiguity: Some((secondary_binding, kind)),
- ..primary_binding.clone()
+ ..(*primary_binding).clone()
})
}
@@ -377,13 +388,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let t = f(self, resolution);
- match resolution.binding() {
- _ if old_binding.is_some() => return t,
- None => return t,
- Some(binding) => match old_binding {
- Some(old_binding) if ptr::eq(old_binding, binding) => return t,
- _ => (binding, t),
- },
+ if old_binding.is_none() && let Some(binding) = resolution.binding() {
+ (binding, t)
+ } else {
+ return t;
}
};
@@ -396,7 +404,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
None => continue,
};
if self.is_accessible_from(binding.vis, scope) {
- let imported_binding = self.import(binding, import);
+ let imported_binding = self.import(binding, *import);
let key = BindingKey { ident, ..key };
let _ = self.try_define(import.parent_scope.module, key, imported_binding);
}
@@ -407,7 +415,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// Define a dummy resolution containing a `Res::Err` as a placeholder for a failed
// or indeterminate resolution, also mark such failed imports as used to avoid duplicate diagnostics.
- fn import_dummy_binding(&mut self, import: &'a Import<'a>, is_indeterminate: bool) {
+ fn import_dummy_binding(&mut self, import: Import<'a>, is_indeterminate: bool) {
if let ImportKind::Single { target, ref target_bindings, .. } = import.kind {
if !(is_indeterminate || target_bindings.iter().all(|binding| binding.get().is_none()))
{
@@ -445,7 +453,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
prev_indeterminate_count = indeterminate_count;
indeterminate_count = 0;
for import in mem::take(&mut self.indeterminate_imports) {
- let import_indeterminate_count = self.resolve_import(&import);
+ let import_indeterminate_count = self.resolve_import(import);
indeterminate_count += import_indeterminate_count;
match import_indeterminate_count {
0 => self.determined_imports.push(import),
@@ -457,7 +465,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
pub(crate) fn finalize_imports(&mut self) {
for module in self.arenas.local_modules().iter() {
- self.finalize_resolutions_in(module);
+ self.finalize_resolutions_in(*module);
}
let mut seen_spans = FxHashSet::default();
@@ -526,35 +534,75 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- pub(crate) fn check_reexport_ambiguities(
+ pub(crate) fn check_hidden_glob_reexports(
&mut self,
- exported_ambiguities: FxHashSet<Interned<'a, NameBinding<'a>>>,
+ exported_ambiguities: FxHashSet<NameBinding<'a>>,
) {
for module in self.arenas.local_modules().iter() {
- module.for_each_child(self, |this, ident, ns, binding| {
- if let NameBindingKind::Import { import, .. } = binding.kind
- && let Some((amb_binding, _)) = binding.ambiguity
- && binding.res() != Res::Err
- && exported_ambiguities.contains(&Interned::new_unchecked(binding))
- {
- this.lint_buffer.buffer_lint_with_diagnostic(
- AMBIGUOUS_GLOB_REEXPORTS,
- import.root_id,
- import.root_span,
- "ambiguous glob re-exports",
- BuiltinLintDiagnostics::AmbiguousGlobReexports {
- name: ident.to_string(),
- namespace: ns.descr().to_string(),
- first_reexport_span: import.root_span,
- duplicate_reexport_span: amb_binding.span,
- },
- );
+ for (key, resolution) in self.resolutions(*module).borrow().iter() {
+ let resolution = resolution.borrow();
+
+ if let Some(binding) = resolution.binding {
+ if let NameBindingKind::Import { import, .. } = binding.kind
+ && let Some((amb_binding, _)) = binding.ambiguity
+ && binding.res() != Res::Err
+ && exported_ambiguities.contains(&binding)
+ {
+ self.lint_buffer.buffer_lint_with_diagnostic(
+ AMBIGUOUS_GLOB_REEXPORTS,
+ import.root_id,
+ import.root_span,
+ "ambiguous glob re-exports",
+ BuiltinLintDiagnostics::AmbiguousGlobReexports {
+ name: key.ident.to_string(),
+ namespace: key.ns.descr().to_string(),
+ first_reexport_span: import.root_span,
+ duplicate_reexport_span: amb_binding.span,
+ },
+ );
+ }
+
+ if let Some(glob_binding) = resolution.shadowed_glob {
+ let binding_id = match binding.kind {
+ NameBindingKind::Res(res) => {
+ Some(self.def_id_to_node_id[res.def_id().expect_local()])
+ }
+ NameBindingKind::Module(module) => {
+ Some(self.def_id_to_node_id[module.def_id().expect_local()])
+ }
+ NameBindingKind::Import { import, .. } => import.id(),
+ };
+
+ if binding.res() != Res::Err
+ && glob_binding.res() != Res::Err
+ && let NameBindingKind::Import { import: glob_import, .. } = glob_binding.kind
+ && let Some(binding_id) = binding_id
+ && let Some(glob_import_id) = glob_import.id()
+ && let glob_import_def_id = self.local_def_id(glob_import_id)
+ && self.effective_visibilities.is_exported(glob_import_def_id)
+ && glob_binding.vis.is_public()
+ && !binding.vis.is_public()
+ {
+ self.lint_buffer.buffer_lint_with_diagnostic(
+ HIDDEN_GLOB_REEXPORTS,
+ binding_id,
+ binding.span,
+ "private item shadows public glob re-export",
+ BuiltinLintDiagnostics::HiddenGlobReexports {
+ name: key.ident.name.to_string(),
+ namespace: key.ns.descr().to_owned(),
+ glob_reexport_span: glob_binding.span,
+ private_item_span: binding.span,
+ },
+ );
+ }
+ }
}
- });
+ }
}
}
- fn throw_unresolved_import_error(&self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) {
+ fn throw_unresolved_import_error(&mut self, errors: Vec<(Import<'_>, UnresolvedImportError)>) {
if errors.is_empty() {
return;
}
@@ -624,6 +672,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
_ => {}
}
}
+
+ match &import.kind {
+ ImportKind::Single { source, .. } => {
+ if let Some(ModuleOrUniformRoot::Module(module)) = import.imported_module.get()
+ && let Some(module) = module.opt_def_id()
+ {
+ self.find_cfg_stripped(&mut diag, &source.name, module)
+ }
+ },
+ _ => {}
+ }
}
diag.emit();
@@ -635,7 +694,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
///
/// Meanwhile, if resolve successful, the resolved bindings are written
/// into the module.
- fn resolve_import(&mut self, import: &'a Import<'a>) -> usize {
+ fn resolve_import(&mut self, import: Import<'a>) -> usize {
debug!(
"(resolving import for module) resolving import `{}::...` in `{}`",
Segment::names_to_string(&import.module_path),
@@ -708,14 +767,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
source_binding @ (Ok(..) | Err(Determined)) => {
if source_binding.is_ok() {
- let msg = format!("`{}` is not directly importable", target);
- struct_span_err!(this.tcx.sess, import.span, E0253, "{}", &msg)
- .span_label(import.span, "cannot be imported directly")
+ this.tcx
+ .sess
+ .create_err(IsNotDirectlyImportable { span: import.span, target })
.emit();
}
let key = BindingKey::new(target, ns);
this.update_resolution(parent, key, |_, resolution| {
- resolution.single_imports.remove(&Interned::new_unchecked(import));
+ resolution.single_imports.remove(&import);
});
}
}
@@ -729,7 +788,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
///
/// Optionally returns an unresolved import error. This error is buffered and used to
/// consolidate multiple unresolved import errors into a single diagnostic.
- fn finalize_import(&mut self, import: &'a Import<'a>) -> Option<UnresolvedImportError> {
+ fn finalize_import(&mut self, import: Import<'a>) -> Option<UnresolvedImportError> {
let orig_vis = import.vis.take();
let ignore_binding = match &import.kind {
ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
@@ -737,6 +796,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
};
let prev_ambiguity_errors_len = self.ambiguity_errors.len();
let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
+
+ // We'll provide more context to the privacy errors later, up to `len`.
+ let privacy_errors_len = self.privacy_errors.len();
+
let path_res = self.resolve_path(
&import.module_path,
None,
@@ -751,25 +814,46 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
PathResult::Module(module) => {
// Consistency checks, analogous to `finalize_macro_resolutions`.
if let Some(initial_module) = import.imported_module.get() {
- if !ModuleOrUniformRoot::same_def(module, initial_module) && no_ambiguity {
+ if module != initial_module && no_ambiguity {
span_bug!(import.span, "inconsistent resolution for an import");
}
} else if self.privacy_errors.is_empty() {
- let msg = "cannot determine resolution for the import";
- let msg_note = "import resolution is stuck, try simplifying other imports";
- self.tcx.sess.struct_span_err(import.span, msg).note(msg_note).emit();
+ self.tcx
+ .sess
+ .create_err(CannotDetermineImportResolution { span: import.span })
+ .emit();
}
module
}
- PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
+ PathResult::Failed {
+ is_error_from_last_segment: false,
+ span,
+ label,
+ suggestion,
+ module,
+ } => {
if no_ambiguity {
assert!(import.imported_module.get().is_none());
- self.report_error(span, ResolutionError::FailedToResolve { label, suggestion });
+ self.report_error(
+ span,
+ ResolutionError::FailedToResolve {
+ last_segment: None,
+ label,
+ suggestion,
+ module,
+ },
+ );
}
return None;
}
- PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => {
+ PathResult::Failed {
+ is_error_from_last_segment: true,
+ span,
+ label,
+ suggestion,
+ ..
+ } => {
if no_ambiguity {
assert!(import.imported_module.get().is_none());
let err = match self.make_path_suggestion(
@@ -800,8 +884,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
return None;
}
- PathResult::NonModule(_) => {
- if no_ambiguity {
+ PathResult::NonModule(partial_res) => {
+ if no_ambiguity && partial_res.full_res() != Some(Res::Err) {
+ // Check if there are no ambiguities and the result is not dummy.
assert!(import.imported_module.get().is_none());
}
// The error was already reported earlier.
@@ -831,7 +916,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
if let ModuleOrUniformRoot::Module(module) = module {
- if ptr::eq(module, import.parent_scope.module) {
+ if module == import.parent_scope.module {
// Importing a module into itself is not allowed.
return Some(UnresolvedImportError {
span: import.span,
@@ -848,14 +933,28 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
&& let Some(max_vis) = max_vis.get()
&& !max_vis.is_at_least(import.expect_vis(), self.tcx)
{
- let msg = "glob import doesn't reexport anything because no candidate is public enough";
- self.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, msg);
+ self.lint_buffer.buffer_lint(UNUSED_IMPORTS, id, import.span, fluent::resolve_glob_import_doesnt_reexport);
}
return None;
}
_ => unreachable!(),
};
+ if self.privacy_errors.len() != privacy_errors_len {
+ // Get the Res for the last element, so that we can point to alternative ways of
+ // importing it if available.
+ let mut path = import.module_path.clone();
+ path.push(Segment::from_ident(ident));
+ if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
+ self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding)
+ {
+ let res = module.res().map(|r| (r, ident));
+ for error in &mut self.privacy_errors[privacy_errors_len..] {
+ error.outermost_res = res;
+ }
+ }
+ }
+
let mut all_ns_err = true;
self.per_ns(|this, ns| {
if !type_ns_only || ns == TypeNS {
@@ -873,7 +972,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
match binding {
Ok(binding) => {
// Consistency checks, analogous to `finalize_macro_resolutions`.
- let initial_binding = source_bindings[ns].get().map(|initial_binding| {
+ let initial_res = source_bindings[ns].get().map(|initial_binding| {
all_ns_err = false;
if let Some(target_binding) = target_bindings[ns].get() {
if target.name == kw::Underscore
@@ -887,29 +986,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
);
}
}
- initial_binding
+ initial_binding.res()
});
let res = binding.res();
- if let Ok(initial_binding) = initial_binding {
- let initial_res = initial_binding.res();
+ if let Ok(initial_res) = initial_res {
if res != initial_res && this.ambiguity_errors.is_empty() {
- this.ambiguity_errors.push(AmbiguityError {
- kind: AmbiguityKind::Import,
- ident,
- b1: initial_binding,
- b2: binding,
- misc1: AmbiguityErrorMisc::None,
- misc2: AmbiguityErrorMisc::None,
- });
+ 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.tcx.sess.struct_span_err(import.span, msg).note(msg_note).emit();
+ this.tcx
+ .sess
+ .create_err(CannotDetermineImportResolution { span: import.span })
+ .emit();
}
}
Err(..) => {
@@ -1067,46 +1158,43 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
msg,
);
} else {
- let error_msg = if crate_private_reexport {
- format!(
- "`{}` is only public within the crate, and cannot be re-exported outside",
- ident
- )
- } else {
- format!("`{}` is private, and cannot be re-exported", ident)
- };
-
if ns == TypeNS {
- let label_msg = if crate_private_reexport {
- format!("re-export of crate public `{}`", ident)
+ let mut err = if crate_private_reexport {
+ self.tcx.sess.create_err(CannotBeReexportedCratePublicNS {
+ span: import.span,
+ ident,
+ })
} else {
- format!("re-export of private `{}`", ident)
+ self.tcx
+ .sess
+ .create_err(CannotBeReexportedPrivateNS { span: import.span, ident })
};
-
- struct_span_err!(self.tcx.sess, import.span, E0365, "{}", error_msg)
- .span_label(import.span, label_msg)
- .note(format!("consider declaring type or module `{}` with `pub`", ident))
- .emit();
+ err.emit();
} else {
- let mut err =
- struct_span_err!(self.tcx.sess, import.span, E0364, "{error_msg}");
+ let mut err = if crate_private_reexport {
+ self.tcx
+ .sess
+ .create_err(CannotBeReexportedCratePublic { span: import.span, ident })
+ } else {
+ self.tcx
+ .sess
+ .create_err(CannotBeReexportedPrivate { span: import.span, ident })
+ };
+
match binding.kind {
NameBindingKind::Res(Res::Def(DefKind::Macro(_), def_id))
// exclude decl_macro
if self.get_macro_by_def_id(def_id).macro_rules =>
{
- err.span_help(
- binding.span,
- "consider adding a `#[macro_export]` to the macro in the imported module",
- );
+ err.subdiagnostic(ConsiderAddingMacroExport {
+ span: binding.span,
+ });
}
_ => {
- err.span_note(
- import.span,
- format!(
- "consider marking `{ident}` as `pub` in the imported module"
- ),
- );
+ err.subdiagnostic(ConsiderMarkingAsPub {
+ span: import.span,
+ ident,
+ });
}
}
err.emit();
@@ -1144,9 +1232,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn check_for_redundant_imports(
&mut self,
ident: Ident,
- import: &'a Import<'a>,
- source_bindings: &PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>,
- target_bindings: &PerNS<Cell<Option<&'a NameBinding<'a>>>>,
+ import: Import<'a>,
+ source_bindings: &PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
+ target_bindings: &PerNS<Cell<Option<NameBinding<'a>>>>,
target: Ident,
) {
// This function is only called for single imports.
@@ -1175,7 +1263,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
match this.early_resolve_ident_in_lexical_scope(
target,
- ScopeSet::All(ns, false),
+ ScopeSet::All(ns),
&import.parent_scope,
None,
false,
@@ -1207,19 +1295,21 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- fn resolve_glob_import(&mut self, import: &'a Import<'a>) {
+ fn resolve_glob_import(&mut self, import: Import<'a>) {
// This function is only called for glob imports.
let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() };
let ModuleOrUniformRoot::Module(module) = import.imported_module.get().unwrap() else {
- self.tcx.sess.span_err(import.span, "cannot glob-import all possible crates");
+ self.tcx.sess.create_err(CannotGlobImportAllCrates {
+ span: import.span,
+ }).emit();
return;
};
if module.is_trait() {
- self.tcx.sess.span_err(import.span, "items in traits are not importable");
+ self.tcx.sess.create_err(ItemsInTraitsAreNotImportable { span: import.span }).emit();
return;
- } else if ptr::eq(module, import.parent_scope.module) {
+ } else if module == import.parent_scope.module {
return;
} else if is_prelude {
self.prelude = Some(module);
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index e06119076..90cb312ed 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -6,6 +6,7 @@
//! If you wonder why there's no `early.rs`, that's because it's split into three files -
//! `build_reduced_graph.rs`, `macros.rs` and `imports.rs`.
+use crate::errors::ImportsCannotReferTo;
use crate::BindingKey;
use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
@@ -1283,7 +1284,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
ident: Ident,
ns: Namespace,
finalize: Option<Finalize>,
- ignore_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
self.r.resolve_ident_in_lexical_scope(
ident,
@@ -1631,9 +1632,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
..
} = &rib.kind
{
- diag.span_help(
- *span,
- "consider introducing a higher-ranked lifetime here with `for<'a>`",
+ diag.multipart_suggestion(
+ "consider introducing a higher-ranked lifetime here",
+ vec![
+ (span.shrink_to_lo(), "for<'a> ".into()),
+ (lifetime.ident.span.shrink_to_hi(), "'a ".into()),
+ ],
+ Applicability::MachineApplicable,
);
break;
}
@@ -2244,12 +2249,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
_ => &[TypeNS],
};
let report_error = |this: &Self, ns| {
- let what = if ns == TypeNS { "type parameters" } else { "local variables" };
if this.should_report_errs() {
+ let what = if ns == TypeNS { "type parameters" } else { "local variables" };
this.r
.tcx
.sess
- .span_err(ident.span, format!("imports cannot refer to {}", what));
+ .create_err(ImportsCannotReferTo { span: ident.span, what })
+ .emit();
}
};
@@ -2966,7 +2972,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
{
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
- let Some((module, _)) = &self.current_trait_ref else { return; };
+ let Some((module, _)) = self.current_trait_ref else { return; };
ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
let key = BindingKey::new(ident, ns);
let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
@@ -3497,7 +3503,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
let report_errors = |this: &mut Self, res: Option<Res>| {
if this.should_report_errs() {
let (err, candidates) =
- this.smart_resolve_report_errors(path, path_span, source, res);
+ this.smart_resolve_report_errors(path, path, path_span, source, res);
let def_id = this.parent_scope.module.nearest_parent_mod();
let instead = res.is_some();
@@ -3524,7 +3530,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
None
};
- this.r.use_injections.push(UseError {
+ let ue = UseError {
err,
candidates,
def_id,
@@ -3532,7 +3538,9 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
suggestion,
path: path.into(),
is_call: source.is_call(),
- });
+ };
+
+ this.r.use_injections.push(ue);
}
PartialRes::new(Res::Err)
@@ -3552,8 +3560,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
_ => return Some(parent_err),
};
- let (mut err, candidates) =
- this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None);
+ let (mut err, candidates) = this.smart_resolve_report_errors(
+ prefix_path,
+ path,
+ path_span,
+ PathSource::Type,
+ None,
+ );
// There are two different error messages user might receive at
// this point:
@@ -3866,8 +3879,22 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
PartialRes::new(module.res().unwrap())
}
- PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
- return Err(respan(span, ResolutionError::FailedToResolve { label, suggestion }));
+ PathResult::Failed {
+ is_error_from_last_segment: false,
+ span,
+ label,
+ suggestion,
+ module,
+ } => {
+ return Err(respan(
+ span,
+ ResolutionError::FailedToResolve {
+ last_segment: None,
+ label,
+ suggestion,
+ module,
+ },
+ ));
}
PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None),
PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"),
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index df6582580..c0e3f1aaf 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -4,20 +4,20 @@ use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseS
use crate::{errors, path_names_to_string};
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
+use rustc_hir::def::Namespace::{self, *};
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
use rustc_ast::{
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
};
-use rustc_ast_pretty::pprust::path_segment_to_string;
+use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
};
use rustc_hir as hir;
-use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::PrimTy;
@@ -29,6 +29,7 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
+use std::borrow::Cow;
use std::iter;
use std::ops::Deref;
@@ -148,6 +149,7 @@ struct BaseError {
span_label: Option<(Span, &'static str)>,
could_be_expr: bool,
suggestion: Option<(Span, &'static str, String)>,
+ module: Option<DefId>,
}
#[derive(Debug)]
@@ -209,19 +211,24 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
_ => false,
},
suggestion: None,
+ module: None,
}
} else {
let item_span = path.last().unwrap().ident.span;
- let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
+ let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {
debug!(?self.diagnostic_metadata.current_impl_items);
debug!(?self.diagnostic_metadata.current_function);
let suggestion = if self.current_trait_ref.is_none()
&& let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
&& let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+ && let FnKind::Fn(_, _, sig, ..) = fn_kind
&& let Some(items) = self.diagnostic_metadata.current_impl_items
&& let Some(item) = items.iter().find(|i| {
if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
&& i.ident.name == item_str.name
+ // don't suggest if the item is in Fn signature arguments
+ // issue #112590
+ && !sig.span.contains(item_span)
{
debug!(?item_str.name);
return true
@@ -246,26 +253,37 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
} else {
None
};
- (String::new(), "this scope".to_string(), suggestion)
+ (String::new(), "this scope".to_string(), None, suggestion)
} else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
if self.r.tcx.sess.edition() > Edition::Edition2015 {
// In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
// which overrides all other expectations of item type
expected = "crate";
- (String::new(), "the list of imported crates".to_string(), None)
+ (String::new(), "the list of imported crates".to_string(), None, None)
} else {
- (String::new(), "the crate root".to_string(), None)
+ (
+ String::new(),
+ "the crate root".to_string(),
+ Some(CRATE_DEF_ID.to_def_id()),
+ None,
+ )
}
} else if path.len() == 2 && path[0].ident.name == kw::Crate {
- (String::new(), "the crate root".to_string(), None)
+ (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)
} else {
let mod_path = &path[..path.len() - 1];
- let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
+ let mod_res = self.resolve_path(mod_path, Some(TypeNS), None);
+ let mod_prefix = match mod_res {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
_ => None,
- }
- .map_or_else(String::new, |res| format!("{} ", res.descr()));
- (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
+ };
+
+ let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);
+
+ let mod_prefix =
+ mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr())));
+
+ (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None)
};
let (fallback_label, suggestion) = if path_str == "async"
@@ -299,21 +317,68 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
span_label: None,
could_be_expr: false,
suggestion,
+ module,
}
}
}
+ /// Try to suggest for a module path that cannot be resolved.
+ /// Such as `fmt::Debug` where `fmt` is not resolved without importing,
+ /// here we search with `lookup_import_candidates` for a module named `fmt`
+ /// with `TypeNS` as namespace.
+ ///
+ /// We need a separate function here because we won't suggest for a path with single segment
+ /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`
+ pub(crate) fn smart_resolve_partial_mod_path_errors(
+ &mut self,
+ prefix_path: &[Segment],
+ path: &[Segment],
+ ) -> Vec<ImportSuggestion> {
+ let next_seg = if path.len() >= prefix_path.len() + 1 && prefix_path.len() == 1 {
+ path.get(prefix_path.len())
+ } else {
+ None
+ };
+ if let Some(segment) = prefix_path.last() &&
+ let Some(next_seg) = next_seg {
+ let candidates = self.r.lookup_import_candidates(
+ segment.ident,
+ Namespace::TypeNS,
+ &self.parent_scope,
+ &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),
+ );
+ // double check next seg is valid
+ candidates
+ .into_iter()
+ .filter(|candidate| {
+ if let Some(def_id) = candidate.did &&
+ let Some(module) = self.r.get_module(def_id) {
+ self.r.resolutions(module).borrow().iter().any(|(key, _r)| {
+ key.ident.name == next_seg.ident.name
+ })
+ } else {
+ false
+ }
+ })
+ .collect::<Vec<_>>()
+ } else {
+ Vec::new()
+ }
+ }
+
/// Handles error reporting for `smart_resolve_path_fragment` function.
/// Creates base error and amends it with one short label and possibly some longer helps/notes.
pub(crate) fn smart_resolve_report_errors(
&mut self,
path: &[Segment],
+ full_path: &[Segment],
span: Span,
source: PathSource<'_>,
res: Option<Res>,
) -> (DiagnosticBuilder<'tcx, ErrorGuaranteed>, Vec<ImportSuggestion>) {
debug!(?res, ?source);
let base_error = self.make_base_error(path, span, source, res);
+
let code = source.error_code(res.is_some());
let mut err = self.r.tcx.sess.struct_span_err_with_code(
base_error.span,
@@ -348,7 +413,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
let (found, candidates) =
- self.try_lookup_name_relaxed(&mut err, source, path, span, res, &base_error);
+ self.try_lookup_name_relaxed(&mut err, source, path, full_path, span, res, &base_error);
if found {
return (err, candidates);
}
@@ -365,6 +430,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
self.err_code_special_cases(&mut err, source, path, span);
+ if let Some(module) = base_error.module {
+ self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);
+ }
+
(err, candidates)
}
@@ -450,6 +519,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
err: &mut Diagnostic,
source: PathSource<'_>,
path: &[Segment],
+ full_path: &[Segment],
span: Span,
res: Option<Res>,
base_error: &BaseError,
@@ -476,7 +546,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
// Try to filter out intrinsics candidates, as long as we have
// some other candidates to suggest.
let intrinsic_candidates: Vec<_> = candidates
- .drain_filter(|sugg| {
+ .extract_if(|sugg| {
let path = path_names_to_string(&sugg.path);
path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
})
@@ -619,6 +689,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
+ if candidates.is_empty() {
+ candidates = self.smart_resolve_partial_mod_path_errors(path, full_path);
+ }
+
return (false, candidates);
}
@@ -1030,7 +1104,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
};
// Confirm that the target is an associated type.
- let (ty, position, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
+ let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
// use this to verify that ident is a type param.
let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
return false;
@@ -1059,7 +1133,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
return false;
}
if let (
- [ast::PathSegment { ident: constrain_ident, args: None, .. }],
+ [ast::PathSegment { args: None, .. }],
[ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)],
) = (&type_param_path.segments[..], &bounds[..])
{
@@ -1067,29 +1141,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
&poly_trait_ref.trait_ref.path.segments[..]
{
if ident.span == span {
+ let Some(new_where_bound_predicate) = mk_where_bound_predicate(path, poly_trait_ref, ty) else { return false; };
err.span_suggestion_verbose(
*where_span,
format!("constrain the associated type to `{}`", ident),
- format!(
- "{}: {}<{} = {}>",
- self.r
- .tcx
- .sess
- .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::<Vec<_>>()
- .join("::"),
- path.segments[position..]
- .iter()
- .map(|segment| path_segment_to_string(segment))
- .collect::<Vec<_>>()
- .join("::"),
- ident,
- ),
+ where_bound_predicate_to_string(&new_where_bound_predicate),
Applicability::MaybeIncorrect,
);
}
@@ -1248,7 +1304,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}),
) if followed_by_brace => {
if let Some(sp) = closing_brace {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.multipart_suggestion(
"surround the struct literal with parentheses",
vec![
@@ -1320,7 +1376,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
);
}
_ => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
}
}
};
@@ -1333,7 +1389,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}))
| PathSource::Struct,
) => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.span_suggestion_verbose(
span.shrink_to_hi(),
"use `!` to invoke the macro",
@@ -1345,7 +1401,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
(Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
}
(Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
err.span_label(span, "type aliases cannot be used as traits");
@@ -1513,7 +1569,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
);
}
(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }, _) if ns == ValueNS => {
- err.span_label(span, fallback_label);
+ err.span_label(span, fallback_label.to_string());
err.note("can't use `Self` as a constructor, you must use the implemented struct");
}
(Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => {
@@ -1537,7 +1593,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
return None;
}
- let resolutions = self.r.resolutions(module);
+ let resolutions = self.r.resolutions(*module);
let targets = resolutions
.borrow()
.iter()
@@ -1829,6 +1885,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
path,
accessible: true,
note: None,
+ via_import: false,
},
));
} else {
@@ -2180,10 +2237,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
None => {
debug!(?param.ident, ?param.ident.span);
let deletion_span = deletion_span();
- // the give lifetime originates from expanded code so we won't be able to remove it #104432
- let lifetime_only_in_expanded_code =
- deletion_span.map(|sp| sp.in_derive_expansion()).unwrap_or(true);
- if !lifetime_only_in_expanded_code {
+
+ // if the lifetime originates from expanded code, we won't be able to remove it #104432
+ if deletion_span.is_some_and(|sp| !sp.in_derive_expansion()) {
self.r.lint_buffer.buffer_lint_with_diagnostic(
lint::builtin::UNUSED_LIFETIMES,
param.id,
@@ -2243,7 +2299,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
&self,
err: &mut Diagnostic,
name: Option<&str>,
- suggest: impl Fn(&mut Diagnostic, bool, Span, &str, String) -> bool,
+ suggest: impl Fn(&mut Diagnostic, bool, Span, Cow<'static, str>, String) -> bool,
) {
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
@@ -2288,22 +2344,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
(span, sugg)
};
if higher_ranked {
- let message = format!(
+ let message = Cow::from(format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
name.unwrap_or("'a"),
- );
- should_continue = suggest(err, true, span, &message, sugg);
+ ));
+ should_continue = suggest(err, true, span, message, sugg);
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
} else if let Some(name) = name {
- let message = format!("consider introducing lifetime `{}` here", name);
- should_continue = suggest(err, false, span, &message, sugg);
+ let message =
+ Cow::from(format!("consider introducing lifetime `{}` here", name));
+ should_continue = suggest(err, false, span, message, sugg);
} else {
- let message = "consider introducing a named lifetime parameter";
- should_continue = suggest(err, false, span, &message, sugg);
+ let message = Cow::from("consider introducing a named lifetime parameter");
+ should_continue = suggest(err, false, span, message, sugg);
}
}
LifetimeRibKind::Item => break,
@@ -2584,6 +2641,70 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
}
}
+fn mk_where_bound_predicate(
+ path: &Path,
+ poly_trait_ref: &ast::PolyTraitRef,
+ ty: &ast::Ty,
+) -> Option<ast::WhereBoundPredicate> {
+ use rustc_span::DUMMY_SP;
+ let modified_segments = {
+ let mut segments = path.segments.clone();
+ let [preceding @ .., second_last, last] = segments.as_mut_slice() else { return None; };
+ let mut segments = ThinVec::from(preceding);
+
+ let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
+ id: DUMMY_NODE_ID,
+ ident: last.ident,
+ gen_args: None,
+ kind: ast::AssocConstraintKind::Equality {
+ term: ast::Term::Ty(ast::ptr::P(ast::Ty {
+ kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
+ id: DUMMY_NODE_ID,
+ span: DUMMY_SP,
+ tokens: None,
+ })),
+ },
+ span: DUMMY_SP,
+ });
+
+ match second_last.args.as_deref_mut() {
+ Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
+ args.push(added_constraint);
+ }
+ Some(_) => return None,
+ None => {
+ second_last.args =
+ Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
+ args: ThinVec::from([added_constraint]),
+ span: DUMMY_SP,
+ })));
+ }
+ }
+
+ segments.push(second_last.clone());
+ segments
+ };
+
+ let new_where_bound_predicate = ast::WhereBoundPredicate {
+ span: DUMMY_SP,
+ bound_generic_params: ThinVec::new(),
+ bounded_ty: ast::ptr::P(ty.clone()),
+ bounds: vec![ast::GenericBound::Trait(
+ ast::PolyTraitRef {
+ bound_generic_params: ThinVec::new(),
+ trait_ref: ast::TraitRef {
+ path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
+ ref_id: DUMMY_NODE_ID,
+ },
+ span: DUMMY_SP,
+ },
+ ast::TraitBoundModifier::None,
+ )],
+ };
+
+ Some(new_where_bound_predicate)
+}
+
/// Report lifetime/lifetime shadowing as an error.
pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
let mut err = struct_span_err!(
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 3d2bd8429..da3d86a47 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -9,11 +9,12 @@
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(assert_matches)]
#![feature(box_patterns)]
-#![feature(drain_filter)]
+#![feature(extract_if)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(never_type)]
+#![feature(rustc_attrs)]
#![recursion_limit = "256"]
#![allow(rustdoc::private_intra_doc_links)]
#![allow(rustc::potential_query_instability)]
@@ -25,6 +26,7 @@ use errors::{
ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst, ParamKindInTyOfConstParam,
};
use rustc_arena::{DroplessArena, TypedArena};
+use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, attr, NodeId, CRATE_NODE_ID};
use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
@@ -60,10 +62,10 @@ use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
-use std::{fmt, ptr};
+use std::fmt;
use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
-use imports::{Import, ImportKind, NameResolution};
+use imports::{Import, ImportData, ImportKind, NameResolution};
use late::{HasGenericParams, PathSource, PatternSource};
use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
@@ -127,10 +129,10 @@ enum Scope<'a> {
/// with different restrictions when looking up the resolution.
/// This enum is currently used only for early resolution (imports and macros),
/// but not for late resolution yet.
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
enum ScopeSet<'a> {
/// All scopes with the given namespace.
- All(Namespace, /*is_import*/ bool),
+ All(Namespace),
/// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros).
AbsolutePath(Namespace),
/// All scopes with macro namespace and the given macro kind restriction.
@@ -171,6 +173,7 @@ enum ImplTraitContext {
Universal(LocalDefId),
}
+#[derive(Debug)]
struct BindingError {
name: Symbol,
origin: BTreeSet<Span>,
@@ -178,6 +181,7 @@ struct BindingError {
could_be_path: bool,
}
+#[derive(Debug)]
enum ResolutionError<'a> {
/// Error E0401: can't use type or const parameters from outer function.
GenericParamsFromOuterFunction(Res, HasGenericParams),
@@ -207,7 +211,12 @@ enum ResolutionError<'a> {
/// Error E0431: `self` import can only appear in an import list with a non-empty prefix.
SelfImportOnlyInImportListWithNonEmptyPrefix,
/// Error E0433: failed to resolve.
- FailedToResolve { label: String, suggestion: Option<Suggestion> },
+ FailedToResolve {
+ last_segment: Option<Symbol>,
+ label: String,
+ suggestion: Option<Suggestion>,
+ module: Option<ModuleOrUniformRoot<'a>>,
+ },
/// Error E0434: can't capture dynamic environment in a fn item.
CannotCaptureDynamicEnvironmentInFnItem,
/// Error E0435: attempt to use a non-constant value in a constant.
@@ -344,7 +353,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
/// forward.
#[derive(Debug)]
enum LexicalScopeBinding<'a> {
- Item(&'a NameBinding<'a>),
+ Item(NameBinding<'a>),
Res(Res),
}
@@ -357,7 +366,7 @@ impl<'a> LexicalScopeBinding<'a> {
}
}
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, PartialEq, Debug)]
enum ModuleOrUniformRoot<'a> {
/// Regular module.
Module(Module<'a>),
@@ -375,23 +384,6 @@ enum ModuleOrUniformRoot<'a> {
CurrentScope,
}
-impl ModuleOrUniformRoot<'_> {
- fn same_def(lhs: Self, rhs: Self) -> bool {
- match (lhs, rhs) {
- (ModuleOrUniformRoot::Module(lhs), ModuleOrUniformRoot::Module(rhs)) => {
- ptr::eq(lhs, rhs)
- }
- (
- ModuleOrUniformRoot::CrateRootAndExternPrelude,
- ModuleOrUniformRoot::CrateRootAndExternPrelude,
- )
- | (ModuleOrUniformRoot::ExternPrelude, ModuleOrUniformRoot::ExternPrelude)
- | (ModuleOrUniformRoot::CurrentScope, ModuleOrUniformRoot::CurrentScope) => true,
- _ => false,
- }
- }
-}
-
#[derive(Debug)]
enum PathResult<'a> {
Module(ModuleOrUniformRoot<'a>),
@@ -402,6 +394,7 @@ enum PathResult<'a> {
label: String,
suggestion: Option<Suggestion>,
is_error_from_last_segment: bool,
+ module: Option<ModuleOrUniformRoot<'a>>,
},
}
@@ -410,11 +403,12 @@ impl<'a> PathResult<'a> {
span: Span,
is_error_from_last_segment: bool,
finalize: bool,
+ module: Option<ModuleOrUniformRoot<'a>>,
label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>),
) -> PathResult<'a> {
let (label, suggestion) =
if finalize { label_and_suggestion() } else { (String::new(), None) };
- PathResult::Failed { span, label, suggestion, is_error_from_last_segment }
+ PathResult::Failed { span, label, suggestion, is_error_from_last_segment, module }
}
}
@@ -508,11 +502,11 @@ struct ModuleData<'a> {
/// Whether `#[no_implicit_prelude]` is active.
no_implicit_prelude: bool,
- glob_importers: RefCell<Vec<&'a Import<'a>>>,
- globs: RefCell<Vec<&'a Import<'a>>>,
+ glob_importers: RefCell<Vec<Import<'a>>>,
+ globs: RefCell<Vec<Import<'a>>>,
/// Used to memoize the traits in this module for faster searches through all traits in scope.
- traits: RefCell<Option<Box<[(Ident, &'a NameBinding<'a>)]>>>,
+ traits: RefCell<Option<Box<[(Ident, NameBinding<'a>)]>>>,
/// Span of the module itself. Used for error reporting.
span: Span,
@@ -520,7 +514,11 @@ struct ModuleData<'a> {
expansion: ExpnId,
}
-type Module<'a> = &'a ModuleData<'a>;
+/// All modules are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+#[derive(Clone, Copy, PartialEq)]
+#[rustc_pass_by_value]
+struct Module<'a>(Interned<'a, ModuleData<'a>>);
impl<'a> ModuleData<'a> {
fn new(
@@ -548,11 +546,13 @@ impl<'a> ModuleData<'a> {
expansion,
}
}
+}
- fn for_each_child<'tcx, R, F>(&'a self, resolver: &mut R, mut f: F)
+impl<'a> Module<'a> {
+ fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F)
where
R: AsMut<Resolver<'a, 'tcx>>,
- F: FnMut(&mut R, Ident, Namespace, &'a NameBinding<'a>),
+ F: FnMut(&mut R, Ident, Namespace, NameBinding<'a>),
{
for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
if let Some(binding) = name_resolution.borrow().binding {
@@ -562,7 +562,7 @@ impl<'a> ModuleData<'a> {
}
/// This modifies `self` in place. The traits will be stored in `self.traits`.
- fn ensure_traits<'tcx, R>(&'a self, resolver: &mut R)
+ fn ensure_traits<'tcx, R>(self, resolver: &mut R)
where
R: AsMut<Resolver<'a, 'tcx>>,
{
@@ -581,7 +581,7 @@ impl<'a> ModuleData<'a> {
}
}
- fn res(&self) -> Option<Res> {
+ fn res(self) -> Option<Res> {
match self.kind {
ModuleKind::Def(kind, def_id, _) => Some(Res::Def(kind, def_id)),
_ => None,
@@ -589,11 +589,11 @@ impl<'a> ModuleData<'a> {
}
// Public for rustdoc.
- fn def_id(&self) -> DefId {
+ fn def_id(self) -> DefId {
self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
}
- fn opt_def_id(&self) -> Option<DefId> {
+ fn opt_def_id(self) -> Option<DefId> {
match self.kind {
ModuleKind::Def(_, def_id, _) => Some(def_id),
_ => None,
@@ -601,15 +601,15 @@ impl<'a> ModuleData<'a> {
}
// `self` resolves to the first module ancestor that `is_normal`.
- fn is_normal(&self) -> bool {
+ fn is_normal(self) -> bool {
matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _))
}
- fn is_trait(&self) -> bool {
+ fn is_trait(self) -> bool {
matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _))
}
- fn nearest_item_scope(&'a self) -> Module<'a> {
+ fn nearest_item_scope(self) -> Module<'a> {
match self.kind {
ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => {
self.parent.expect("enum or trait module without a parent")
@@ -620,15 +620,15 @@ impl<'a> ModuleData<'a> {
/// The [`DefId`] of the nearest `mod` item ancestor (which may be this module).
/// This may be the crate root.
- fn nearest_parent_mod(&self) -> DefId {
+ fn nearest_parent_mod(self) -> DefId {
match self.kind {
ModuleKind::Def(DefKind::Mod, def_id, _) => def_id,
_ => self.parent.expect("non-root module without parent").nearest_parent_mod(),
}
}
- fn is_ancestor_of(&self, mut other: &Self) -> bool {
- while !ptr::eq(self, other) {
+ fn is_ancestor_of(self, mut other: Self) -> bool {
+ while self != other {
if let Some(parent) = other.parent {
other = parent;
} else {
@@ -639,7 +639,15 @@ impl<'a> ModuleData<'a> {
}
}
-impl<'a> fmt::Debug for ModuleData<'a> {
+impl<'a> std::ops::Deref for Module<'a> {
+ type Target = ModuleData<'a>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl<'a> fmt::Debug for Module<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.res())
}
@@ -647,20 +655,24 @@ impl<'a> fmt::Debug for ModuleData<'a> {
/// Records a possibly-private value, type, or module definition.
#[derive(Clone, Debug)]
-struct NameBinding<'a> {
+struct NameBindingData<'a> {
kind: NameBindingKind<'a>,
- ambiguity: Option<(&'a NameBinding<'a>, AmbiguityKind)>,
+ ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>,
expansion: LocalExpnId,
span: Span,
vis: ty::Visibility<DefId>,
}
+/// All name bindings are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+type NameBinding<'a> = Interned<'a, NameBindingData<'a>>;
+
trait ToNameBinding<'a> {
- fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a>;
+ fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a>;
}
-impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> {
- fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> &'a NameBinding<'a> {
+impl<'a> ToNameBinding<'a> for NameBinding<'a> {
+ fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> NameBinding<'a> {
self
}
}
@@ -669,7 +681,7 @@ impl<'a> ToNameBinding<'a> for &'a NameBinding<'a> {
enum NameBindingKind<'a> {
Res(Res),
Module(Module<'a>),
- Import { binding: &'a NameBinding<'a>, import: &'a Import<'a>, used: Cell<bool> },
+ Import { binding: NameBinding<'a>, import: Import<'a>, used: Cell<bool> },
}
impl<'a> NameBindingKind<'a> {
@@ -679,12 +691,16 @@ impl<'a> NameBindingKind<'a> {
}
}
+#[derive(Debug)]
struct PrivacyError<'a> {
ident: Ident,
- binding: &'a NameBinding<'a>,
+ binding: NameBinding<'a>,
dedup_span: Span,
+ outermost_res: Option<(Res, Ident)>,
+ parent_scope: ParentScope<'a>,
}
+#[derive(Debug)]
struct UseError<'a> {
err: DiagnosticBuilder<'a, ErrorGuaranteed>,
/// Candidates which user could `use` to access the missing type.
@@ -704,7 +720,6 @@ struct UseError<'a> {
#[derive(Clone, Copy, PartialEq, Debug)]
enum AmbiguityKind {
- Import,
BuiltinAttr,
DeriveHelper,
MacroRulesVsModularized,
@@ -717,7 +732,6 @@ enum AmbiguityKind {
impl AmbiguityKind {
fn descr(self) -> &'static str {
match self {
- AmbiguityKind::Import => "multiple potential import sources",
AmbiguityKind::BuiltinAttr => "a name conflict with a builtin attribute",
AmbiguityKind::DeriveHelper => "a name conflict with a derive helper attribute",
AmbiguityKind::MacroRulesVsModularized => {
@@ -749,13 +763,13 @@ enum AmbiguityErrorMisc {
struct AmbiguityError<'a> {
kind: AmbiguityKind,
ident: Ident,
- b1: &'a NameBinding<'a>,
- b2: &'a NameBinding<'a>,
+ b1: NameBinding<'a>,
+ b2: NameBinding<'a>,
misc1: AmbiguityErrorMisc,
misc2: AmbiguityErrorMisc,
}
-impl<'a> NameBinding<'a> {
+impl<'a> NameBindingData<'a> {
fn module(&self) -> Option<Module<'a>> {
match self.kind {
NameBindingKind::Module(module) => Some(module),
@@ -793,14 +807,12 @@ impl<'a> NameBinding<'a> {
fn is_extern_crate(&self) -> bool {
match self.kind {
- NameBindingKind::Import {
- import: &Import { kind: ImportKind::ExternCrate { .. }, .. },
- ..
- } => true,
- NameBindingKind::Module(&ModuleData {
- kind: ModuleKind::Def(DefKind::Mod, def_id, _),
- ..
- }) => def_id.is_crate_root(),
+ NameBindingKind::Import { import, .. } => {
+ matches!(import.kind, ImportKind::ExternCrate { .. })
+ }
+ NameBindingKind::Module(module)
+ if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind
+ => def_id.is_crate_root(),
_ => false,
}
}
@@ -843,7 +855,7 @@ impl<'a> NameBinding<'a> {
fn may_appear_after(
&self,
invoc_parent_expansion: LocalExpnId,
- binding: &NameBinding<'_>,
+ binding: NameBinding<'_>,
) -> bool {
// self > max(invoc, binding) => !(self <= invoc || self <= binding)
// Expansions are partially ordered, so "may appear after" is an inversion of
@@ -860,7 +872,7 @@ impl<'a> NameBinding<'a> {
#[derive(Default, Clone)]
struct ExternPreludeEntry<'a> {
- extern_crate_item: Option<&'a NameBinding<'a>>,
+ extern_crate_item: Option<NameBinding<'a>>,
introduced_by_item: bool,
}
@@ -905,10 +917,10 @@ pub struct Resolver<'a, 'tcx> {
field_visibility_spans: FxHashMap<DefId, Vec<Span>>,
/// All imports known to succeed or fail.
- determined_imports: Vec<&'a Import<'a>>,
+ determined_imports: Vec<Import<'a>>,
/// All non-determined imports.
- indeterminate_imports: Vec<&'a Import<'a>>,
+ indeterminate_imports: Vec<Import<'a>>,
// Spans for local variables found during pattern resolution.
// Used for suggestions during error reporting.
@@ -950,7 +962,7 @@ pub struct Resolver<'a, 'tcx> {
/// language items.
empty_module: Module<'a>,
module_map: FxHashMap<DefId, Module<'a>>,
- binding_parent_modules: FxHashMap<Interned<'a, NameBinding<'a>>, Module<'a>>,
+ binding_parent_modules: FxHashMap<NameBinding<'a>, Module<'a>>,
underscore_disambiguator: u32,
@@ -972,7 +984,7 @@ pub struct Resolver<'a, 'tcx> {
macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>,
arenas: &'a ResolverArenas<'a>,
- dummy_binding: &'a NameBinding<'a>,
+ dummy_binding: NameBinding<'a>,
used_extern_options: FxHashSet<Symbol>,
macro_names: FxHashSet<Ident>,
@@ -981,7 +993,7 @@ pub struct Resolver<'a, 'tcx> {
/// the surface (`macro` items in libcore), but are actually attributes or derives.
builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
registered_tools: &'tcx RegisteredTools,
- macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
+ macro_use_prelude: FxHashMap<Symbol, NameBinding<'a>>,
macro_map: FxHashMap<DefId, MacroData>,
dummy_ext_bang: Lrc<SyntaxExtension>,
dummy_ext_derive: Lrc<SyntaxExtension>,
@@ -993,7 +1005,7 @@ pub struct Resolver<'a, 'tcx> {
proc_macro_stubs: FxHashSet<LocalDefId>,
/// Traces collected during macro resolution and validated when it's complete.
single_segment_macro_resolutions:
- Vec<(Ident, MacroKind, ParentScope<'a>, Option<&'a NameBinding<'a>>)>,
+ Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
multi_segment_macro_resolutions:
Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
@@ -1018,7 +1030,7 @@ pub struct Resolver<'a, 'tcx> {
/// Avoid duplicated errors for "name already defined".
name_already_seen: FxHashMap<Symbol, Span>,
- potentially_unused_imports: Vec<&'a Import<'a>>,
+ potentially_unused_imports: Vec<Import<'a>>,
/// Table for mapping struct IDs into struct constructor IDs,
/// it's not used during normal resolution, only for better error reporting.
@@ -1059,6 +1071,9 @@ pub struct Resolver<'a, 'tcx> {
/// Whether lifetime elision was successful.
lifetime_elision_allowed: FxHashSet<NodeId>,
+ /// Names of items that were stripped out via cfg with their corresponding cfg meta item.
+ stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,
+
effective_visibilities: EffectiveVisibilities,
doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
@@ -1070,7 +1085,7 @@ pub struct Resolver<'a, 'tcx> {
pub struct ResolverArenas<'a> {
modules: TypedArena<ModuleData<'a>>,
local_modules: RefCell<Vec<Module<'a>>>,
- imports: TypedArena<Import<'a>>,
+ imports: TypedArena<ImportData<'a>>,
name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
ast_paths: TypedArena<ast::Path>,
dropless: DroplessArena,
@@ -1086,8 +1101,13 @@ impl<'a> ResolverArenas<'a> {
no_implicit_prelude: bool,
module_map: &mut FxHashMap<DefId, Module<'a>>,
) -> Module<'a> {
- let module =
- self.modules.alloc(ModuleData::new(parent, kind, expn_id, span, no_implicit_prelude));
+ let module = Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new(
+ parent,
+ kind,
+ expn_id,
+ span,
+ no_implicit_prelude,
+ ))));
let def_id = module.opt_def_id();
if def_id.map_or(true, |def_id| def_id.is_local()) {
self.local_modules.borrow_mut().push(module);
@@ -1100,11 +1120,11 @@ impl<'a> ResolverArenas<'a> {
fn local_modules(&'a self) -> std::cell::Ref<'a, Vec<Module<'a>>> {
self.local_modules.borrow()
}
- fn alloc_name_binding(&'a self, name_binding: NameBinding<'a>) -> &'a NameBinding<'a> {
- self.dropless.alloc(name_binding)
+ fn alloc_name_binding(&'a self, name_binding: NameBindingData<'a>) -> NameBinding<'a> {
+ Interned::new_unchecked(self.dropless.alloc(name_binding))
}
- fn alloc_import(&'a self, import: Import<'a>) -> &'a Import<'_> {
- self.imports.alloc(import)
+ fn alloc_import(&'a self, import: ImportData<'a>) -> Import<'a> {
+ Interned::new_unchecked(self.imports.alloc(import))
}
fn alloc_name_resolution(&'a self) -> &'a RefCell<NameResolution<'a>> {
self.name_resolutions.alloc(Default::default())
@@ -1299,7 +1319,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
macro_expanded_macro_export_errors: BTreeSet::new(),
arenas,
- dummy_binding: arenas.alloc_name_binding(NameBinding {
+ dummy_binding: arenas.alloc_name_binding(NameBindingData {
kind: NameBindingKind::Res(Res::Err),
ambiguity: None,
expansion: LocalExpnId::ROOT,
@@ -1353,6 +1373,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
proc_macros: Default::default(),
confused_type_with_std_module: Default::default(),
lifetime_elision_allowed: Default::default(),
+ stripped_cfg_items: Default::default(),
effective_visibilities: Default::default(),
doc_link_resolutions: Default::default(),
doc_link_traits_in_scope: Default::default(),
@@ -1410,6 +1431,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let main_def = self.main_def;
let confused_type_with_std_module = self.confused_type_with_std_module;
let effective_visibilities = self.effective_visibilities;
+
+ self.tcx.feed_local_crate().stripped_cfg_items(self.tcx.arena.alloc_from_iter(
+ self.stripped_cfg_items.into_iter().filter_map(|item| {
+ let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.to_def_id();
+ Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg })
+ }),
+ ));
+
let global_ctxt = ResolverGlobalCtxt {
expn_that_defined,
visibilities,
@@ -1496,10 +1525,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let exported_ambiguities = self.tcx.sess.time("compute_effective_visibilities", || {
EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate)
});
- self.tcx.sess.time("check_reexport_ambiguities", || {
- self.check_reexport_ambiguities(exported_ambiguities)
+ self.tcx.sess.time("check_hidden_glob_reexports", || {
+ self.check_hidden_glob_reexports(exported_ambiguities)
});
- self.tcx.sess.time("finalize_macro_resolutions", || self.finalize_macro_resolutions());
+ self.tcx
+ .sess
+ .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate));
self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate));
self.tcx.sess.time("resolve_main", || self.resolve_main());
self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate));
@@ -1529,7 +1560,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
}
- self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| {
+ self.visit_scopes(ScopeSet::All(TypeNS), parent_scope, ctxt, |this, scope, _, _| {
match scope {
Scope::Module(module, _) => {
this.traits_in_module(module, assoc_item, &mut found_traits);
@@ -1598,7 +1629,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
self.maybe_unused_trait_imports.insert(def_id);
import_ids.push(def_id);
}
- self.add_to_glob_map(&import, trait_name);
+ self.add_to_glob_map(*import, trait_name);
kind = &binding.kind;
}
import_ids
@@ -1620,7 +1651,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
module.populate_on_access.set(false);
self.build_reduced_graph_external(module);
}
- &module.lazy_resolutions
+ &module.0.0.lazy_resolutions
}
fn resolution(
@@ -1653,12 +1684,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
false
}
- fn record_use(
- &mut self,
- ident: Ident,
- used_binding: &'a NameBinding<'a>,
- is_lexical_scope: bool,
- ) {
+ fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, is_lexical_scope: bool) {
if let Some((b2, kind)) = used_binding.ambiguity {
let ambiguity_error = AmbiguityError {
kind,
@@ -1678,10 +1704,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
// but not introduce it, as used if they are accessed from lexical scope.
if is_lexical_scope {
if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
- if let Some(crate_item) = entry.extern_crate_item {
- if ptr::eq(used_binding, crate_item) && !entry.introduced_by_item {
- return;
- }
+ if !entry.introduced_by_item && entry.extern_crate_item == Some(used_binding) {
+ return;
}
}
}
@@ -1690,13 +1714,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if let Some(id) = import.id() {
self.used_imports.insert(id);
}
- self.add_to_glob_map(&import, ident);
+ self.add_to_glob_map(import, ident);
self.record_use(ident, binding, false);
}
}
#[inline]
- fn add_to_glob_map(&mut self, import: &Import<'_>, ident: Ident) {
+ fn add_to_glob_map(&mut self, import: Import<'_>, ident: Ident) {
if let ImportKind::Glob { id, .. } = import.kind {
let def_id = self.local_def_id(id);
self.glob_map.entry(def_id).or_default().insert(ident.name);
@@ -1805,11 +1829,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
vis.is_accessible_from(module.nearest_parent_mod(), self.tcx)
}
- fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) {
- if let Some(old_module) =
- self.binding_parent_modules.insert(Interned::new_unchecked(binding), module)
- {
- if !ptr::eq(module, old_module) {
+ fn set_binding_parent_module(&mut self, binding: NameBinding<'a>, module: Module<'a>) {
+ if let Some(old_module) = self.binding_parent_modules.insert(binding, module) {
+ if module != old_module {
span_bug!(binding.span, "parent module is reset for binding");
}
}
@@ -1817,25 +1839,25 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn disambiguate_macro_rules_vs_modularized(
&self,
- macro_rules: &'a NameBinding<'a>,
- modularized: &'a NameBinding<'a>,
+ macro_rules: NameBinding<'a>,
+ modularized: NameBinding<'a>,
) -> bool {
// Some non-controversial subset of ambiguities "modularized macro name" vs "macro_rules"
// is disambiguated to mitigate regressions from macro modularization.
// Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
match (
- self.binding_parent_modules.get(&Interned::new_unchecked(macro_rules)),
- self.binding_parent_modules.get(&Interned::new_unchecked(modularized)),
+ self.binding_parent_modules.get(&macro_rules),
+ self.binding_parent_modules.get(&modularized),
) {
(Some(macro_rules), Some(modularized)) => {
macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod()
- && modularized.is_ancestor_of(macro_rules)
+ && modularized.is_ancestor_of(*macro_rules)
}
_ => false,
}
}
- fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<&'a NameBinding<'a>> {
+ fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<NameBinding<'a>> {
if ident.is_path_segment_keyword() {
// Make sure `self`, `super` etc produce an error when passed to here.
return None;
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index df5c16a93..d16b7902f 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1,12 +1,15 @@
//! A bunch of methods and structures more or less related to resolving macros and
//! interface provided by `Resolver` to macro expander.
-use crate::errors::{self, AddAsNonDerive, MacroExpectedFound, RemoveSurroundingDerive};
+use crate::errors::{
+ self, AddAsNonDerive, CannotFindIdentInThisScope, MacroExpectedFound, RemoveSurroundingDerive,
+};
use crate::Namespace::*;
use crate::{BuiltinMacroState, Determinacy};
use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment};
-use rustc_ast::{self as ast, attr, Inline, ItemKind, ModKind, NodeId};
+use rustc_ast::expand::StrippedCfgItem;
+use rustc_ast::{self as ast, attr, Crate, Inline, ItemKind, ModKind, NodeId};
use rustc_ast_pretty::pprust;
use rustc_attr::StabilityLevel;
use rustc_data_structures::intern::Interned;
@@ -39,7 +42,7 @@ type Res = def::Res<NodeId>;
/// Not modularized, can shadow previous `macro_rules` bindings, etc.
#[derive(Debug)]
pub(crate) struct MacroRulesBinding<'a> {
- pub(crate) binding: &'a NameBinding<'a>,
+ pub(crate) binding: NameBinding<'a>,
/// `macro_rules` scope into which the `macro_rules` item was planted.
pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'a>,
pub(crate) ident: Ident,
@@ -465,6 +468,10 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
self.proc_macros.push(id)
}
+ fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) {
+ self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg });
+ }
+
fn registered_tools(&self) -> &RegisteredTools {
&self.registered_tools
}
@@ -638,7 +645,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
res
} else {
- let scope_set = kind.map_or(ScopeSet::All(MacroNS, false), ScopeSet::Macro);
+ let scope_set = kind.map_or(ScopeSet::All(MacroNS), ScopeSet::Macro);
let binding = self.early_resolve_ident_in_lexical_scope(
path[0].ident,
scope_set,
@@ -669,7 +676,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext), res))
}
- pub(crate) fn finalize_macro_resolutions(&mut self) {
+ pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) {
let check_consistency = |this: &mut Self,
path: &[Segment],
span,
@@ -721,7 +728,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
let mut suggestion = None;
- let (span, label) = if let PathResult::Failed { span, label, .. } = path_res {
+ let (span, label, module) = if let PathResult::Failed { span, label, module, .. } = path_res {
// try to suggest if it's not a macro, maybe a function
if let PathResult::NonModule(partial_res) = self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope)
&& partial_res.unresolved_segments() == 0 {
@@ -733,7 +740,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
Applicability::MaybeIncorrect
));
}
- (span, label)
+ (span, label, module)
} else {
(
path_span,
@@ -742,11 +749,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
kind.article(),
kind.descr()
),
+ None,
)
};
self.report_error(
span,
- ResolutionError::FailedToResolve { label, suggestion },
+ ResolutionError::FailedToResolve { last_segment: path.last().map(|segment| segment.ident.name), label, suggestion, module },
);
}
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
@@ -787,9 +795,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
}
Err(..) => {
let expected = kind.descr_expected();
- let msg = format!("cannot find {} `{}` in this scope", expected, ident);
- let mut err = self.tcx.sess.struct_span_err(ident.span, msg);
- self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident);
+
+ let mut err = self.tcx.sess.create_err(CannotFindIdentInThisScope {
+ span: ident.span,
+ expected,
+ ident,
+ });
+
+ self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate);
err.emit();
}
}
@@ -827,7 +840,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
if !is_allowed(feature) && !allowed_by_implication {
let lint_buffer = &mut self.lint_buffer;
let soft_handler =
- |lint, span, msg: &_| lint_buffer.buffer_lint(lint, node_id, span, msg);
+ |lint, span, msg: String| lint_buffer.buffer_lint(lint, node_id, span, msg);
stability::report_unstable(
self.tcx.sess,
feature,
@@ -846,7 +859,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
let (message, lint) = stability::deprecation_message_and_lint(depr, "macro", &path);
stability::early_report_deprecation(
&mut self.lint_buffer,
- &message,
+ message,
depr.suggestion,
lint,
span,
@@ -857,7 +870,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
fn prohibit_imported_non_macro_attrs(
&self,
- binding: Option<&'a NameBinding<'a>>,
+ binding: Option<NameBinding<'a>>,
res: Option<Res>,
span: Span,
) {
diff --git a/compiler/rustc_serialize/Cargo.toml b/compiler/rustc_serialize/Cargo.toml
index 604678068..46b923e8c 100644
--- a/compiler/rustc_serialize/Cargo.toml
+++ b/compiler/rustc_serialize/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.0.0"
edition = "2021"
[dependencies]
-indexmap = "1.9.3"
+indexmap = "2.0.0"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
thin-vec = "0.2.12"
diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml
index 3af83aaaa..1291d1454 100644
--- a/compiler/rustc_session/Cargo.toml
+++ b/compiler/rustc_session/Cargo.toml
@@ -4,6 +4,8 @@ version = "0.0.0"
edition = "2021"
[dependencies]
+atty = "0.2.13"
+bitflags = "1.2.1"
getopts = "0.2"
rustc_macros = { path = "../rustc_macros" }
tracing = "0.1"
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 5a0b8f9f7..4897bd8d5 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -27,6 +27,10 @@ session_feature_gate_error = {$explain}
session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions
session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
+
+session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target
+ .note = compatible flavors are: {$compatible_list}
+
session_incorrect_cgu_reuse_type =
CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
[one] {"at least "}
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index 0dfee92f4..cabe1c96b 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -1,5 +1,6 @@
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lock;
+use rustc_span::def_id::DefId;
use rustc_span::Symbol;
use rustc_target::abi::{Align, Size};
use std::cmp;
@@ -65,9 +66,29 @@ pub struct TypeSizeInfo {
pub variants: Vec<VariantInfo>,
}
+pub struct VTableSizeInfo {
+ pub trait_name: String,
+
+ /// Number of entries in a vtable with the current algorithm
+ /// (i.e. with upcasting).
+ pub entries: usize,
+
+ /// Number of entries in a vtable, as-if we did not have trait upcasting.
+ pub entries_ignoring_upcasting: usize,
+
+ /// Number of entries in a vtable needed solely for upcasting
+ /// (i.e. `entries - entries_ignoring_upcasting`).
+ pub entries_for_upcasting: usize,
+
+ /// Cost of having upcasting in % relative to the number of entries without
+ /// upcasting (i.e. `entries_for_upcasting / entries_ignoring_upcasting * 100%`).
+ pub upcasting_cost_percent: f64,
+}
+
#[derive(Default)]
pub struct CodeStats {
type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
+ vtable_sizes: Lock<FxHashMap<DefId, VTableSizeInfo>>,
}
impl CodeStats {
@@ -101,6 +122,14 @@ impl CodeStats {
self.type_sizes.borrow_mut().insert(info);
}
+ pub fn record_vtable_size(&self, trait_did: DefId, trait_name: &str, info: VTableSizeInfo) {
+ let prev = self.vtable_sizes.lock().insert(trait_did, info);
+ assert!(
+ prev.is_none(),
+ "size of vtable for `{trait_name}` ({trait_did:?}) is already recorded"
+ );
+ }
+
pub fn print_type_sizes(&self) {
let type_sizes = self.type_sizes.borrow();
let mut sorted: Vec<_> = type_sizes.iter().collect();
@@ -196,4 +225,33 @@ impl CodeStats {
}
}
}
+
+ pub fn print_vtable_sizes(&self, crate_name: &str) {
+ let mut infos = std::mem::take(&mut *self.vtable_sizes.lock())
+ .into_iter()
+ .map(|(_did, stats)| stats)
+ .collect::<Vec<_>>();
+
+ // Primary sort: cost % in reverse order (from largest to smallest)
+ // Secondary sort: trait_name
+ infos.sort_by(|a, b| {
+ a.upcasting_cost_percent
+ .total_cmp(&b.upcasting_cost_percent)
+ .reverse()
+ .then_with(|| a.trait_name.cmp(&b.trait_name))
+ });
+
+ for VTableSizeInfo {
+ trait_name,
+ entries,
+ entries_ignoring_upcasting,
+ entries_for_upcasting,
+ upcasting_cost_percent,
+ } in infos
+ {
+ println!(
+ r#"print-vtable-sizes {{ "crate_name": "{crate_name}", "trait_name": "{trait_name}", "entries": "{entries}", "entries_ignoring_upcasting": "{entries_ignoring_upcasting}", "entries_for_upcasting": "{entries_for_upcasting}", "upcasting_cost_percent": "{upcasting_cost_percent}" }}"#
+ );
+ }
+ }
}
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 6c8c8e484..f97cb3440 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -5,11 +5,10 @@ pub use crate::options::*;
use crate::search_paths::SearchPath;
use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
-use crate::{early_error, early_warn, Session};
use crate::{lint, HashStableContext};
+use crate::{EarlyErrorHandler, Session};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-
use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
use rustc_target::abi::Align;
use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
@@ -30,6 +29,7 @@ use std::collections::btree_map::{
Iter as BTreeMapIter, Keys as BTreeMapKeysIter, Values as BTreeMapValuesIter,
};
use std::collections::{BTreeMap, BTreeSet};
+use std::ffi::OsStr;
use std::fmt;
use std::hash::Hash;
use std::iter;
@@ -200,6 +200,128 @@ pub enum LinkerPluginLto {
Disabled,
}
+impl LinkerPluginLto {
+ pub fn enabled(&self) -> bool {
+ match *self {
+ LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
+ LinkerPluginLto::Disabled => false,
+ }
+ }
+}
+
+/// The different values `-C link-self-contained` can take: a list of individually enabled or
+/// disabled components used during linking, coming from the rustc distribution, instead of being
+/// found somewhere on the host system.
+///
+/// They can be set in bulk via `-C link-self-contained=yes|y|on` or `-C
+/// link-self-contained=no|n|off`, and those boolean values are the historical defaults.
+///
+/// But each component is fine-grained, and can be unstably targeted, to use:
+/// - some CRT objects
+/// - the libc static library
+/// - libgcc/libunwind libraries
+/// - a linker we distribute
+/// - some sanitizer runtime libraries
+/// - all other MinGW libraries and Windows import libs
+///
+#[derive(Default, Clone, PartialEq, Debug)]
+pub struct LinkSelfContained {
+ /// Whether the user explicitly set `-C link-self-contained` on or off, the historical values.
+ /// Used for compatibility with the existing opt-in and target inference.
+ pub explicitly_set: Option<bool>,
+
+ /// The components that are enabled.
+ components: LinkSelfContainedComponents,
+}
+
+bitflags::bitflags! {
+ #[derive(Default)]
+ /// The `-C link-self-contained` components that can individually be enabled or disabled.
+ pub struct LinkSelfContainedComponents: u8 {
+ /// CRT objects (e.g. on `windows-gnu`, `musl`, `wasi` targets)
+ const CRT_OBJECTS = 1 << 0;
+ /// libc static library (e.g. on `musl`, `wasi` targets)
+ const LIBC = 1 << 1;
+ /// libgcc/libunwind (e.g. on `windows-gnu`, `fuchsia`, `fortanix`, `gnullvm` targets)
+ const UNWIND = 1 << 2;
+ /// Linker, dlltool, and their necessary libraries (e.g. on `windows-gnu` and for `rust-lld`)
+ const LINKER = 1 << 3;
+ /// Sanitizer runtime libraries
+ const SANITIZERS = 1 << 4;
+ /// Other MinGW libs and Windows import libs
+ const MINGW = 1 << 5;
+ }
+}
+
+impl FromStr for LinkSelfContainedComponents {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(match s {
+ "crto" => LinkSelfContainedComponents::CRT_OBJECTS,
+ "libc" => LinkSelfContainedComponents::LIBC,
+ "unwind" => LinkSelfContainedComponents::UNWIND,
+ "linker" => LinkSelfContainedComponents::LINKER,
+ "sanitizers" => LinkSelfContainedComponents::SANITIZERS,
+ "mingw" => LinkSelfContainedComponents::MINGW,
+ _ => return Err(()),
+ })
+ }
+}
+
+impl LinkSelfContained {
+ /// Incorporates an enabled or disabled component as specified on the CLI, if possible.
+ /// For example: `+linker`, and `-crto`.
+ pub(crate) fn handle_cli_component(&mut self, component: &str) -> Result<(), ()> {
+ // Note that for example `-Cself-contained=y -Cself-contained=-linker` is not an explicit
+ // set of all values like `y` or `n` used to be. Therefore, if this flag had previously been
+ // set in bulk with its historical values, then manually setting a component clears that
+ // `explicitly_set` state.
+ if let Some(component_to_enable) = component.strip_prefix("+") {
+ self.explicitly_set = None;
+ self.components.insert(component_to_enable.parse()?);
+ Ok(())
+ } else if let Some(component_to_disable) = component.strip_prefix("-") {
+ self.explicitly_set = None;
+ self.components.remove(component_to_disable.parse()?);
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// Turns all components on or off and records that this was done explicitly for compatibility
+ /// purposes.
+ pub(crate) fn set_all_explicitly(&mut self, enabled: bool) {
+ self.explicitly_set = Some(enabled);
+ self.components = if enabled {
+ LinkSelfContainedComponents::all()
+ } else {
+ LinkSelfContainedComponents::empty()
+ };
+ }
+
+ /// Helper creating a fully enabled `LinkSelfContained` instance. Used in tests.
+ pub fn on() -> Self {
+ let mut on = LinkSelfContained::default();
+ on.set_all_explicitly(true);
+ on
+ }
+
+ /// To help checking CLI usage while some of the values are unstable: returns whether one of the
+ /// components was set individually. This would also require the `-Zunstable-options` flag, to
+ /// be allowed.
+ fn are_unstable_variants_set(&self) -> bool {
+ let any_component_set = !self.components.is_empty();
+ self.explicitly_set.is_none() && any_component_set
+ }
+
+ /// Returns whether the self-contained linker component is enabled.
+ pub fn linker(&self) -> bool {
+ self.components.contains(LinkSelfContainedComponents::LINKER)
+ }
+}
+
/// Used with `-Z assert-incr-state`.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum IncrementalStateAssertion {
@@ -212,15 +334,6 @@ pub enum IncrementalStateAssertion {
NotLoaded,
}
-impl LinkerPluginLto {
- pub fn enabled(&self) -> bool {
- match *self {
- LinkerPluginLto::LinkerPlugin(_) | LinkerPluginLto::LinkerPluginAuto => true,
- LinkerPluginLto::Disabled => false,
- }
- }
-}
-
/// The different settings that can be enabled via the `-Z location-detail` flag.
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
pub struct LocationDetail {
@@ -311,7 +424,9 @@ pub enum OutputType {
}
// Safety: Trivial C-Style enums have a stable sort order across compilation sessions.
-unsafe impl StableOrd for OutputType {}
+unsafe impl StableOrd for OutputType {
+ const CAN_USE_UNSTABLE_SORT: bool = true;
+}
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
type KeyType = Self;
@@ -333,7 +448,7 @@ impl OutputType {
}
}
- fn shorthand(&self) -> &'static str {
+ pub fn shorthand(&self) -> &'static str {
match *self {
OutputType::Bitcode => "llvm-bc",
OutputType::Assembly => "asm",
@@ -386,6 +501,18 @@ impl OutputType {
OutputType::Exe => "",
}
}
+
+ pub fn is_text_output(&self) -> bool {
+ match *self {
+ OutputType::Assembly
+ | OutputType::LlvmAssembly
+ | OutputType::Mir
+ | OutputType::DepInfo => true,
+ OutputType::Bitcode | OutputType::Object | OutputType::Metadata | OutputType::Exe => {
+ false
+ }
+ }
+ }
}
/// The type of diagnostics output to generate.
@@ -438,14 +565,14 @@ pub enum ResolveDocLinks {
/// dependency tracking for command-line arguments. Also only hash keys, since tracking
/// should only depend on the output types, not the paths they're written to.
#[derive(Clone, Debug, Hash, HashStable_Generic)]
-pub struct OutputTypes(BTreeMap<OutputType, Option<PathBuf>>);
+pub struct OutputTypes(BTreeMap<OutputType, Option<OutFileName>>);
impl OutputTypes {
- pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes {
+ pub fn new(entries: &[(OutputType, Option<OutFileName>)]) -> OutputTypes {
OutputTypes(BTreeMap::from_iter(entries.iter().map(|&(k, ref v)| (k, v.clone()))))
}
- pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> {
+ pub fn get(&self, key: &OutputType) -> Option<&Option<OutFileName>> {
self.0.get(key)
}
@@ -453,11 +580,15 @@ impl OutputTypes {
self.0.contains_key(key)
}
- pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<PathBuf>> {
+ pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> {
+ self.0.iter()
+ }
+
+ pub fn keys(&self) -> BTreeMapKeysIter<'_, OutputType, Option<OutFileName>> {
self.0.keys()
}
- pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<PathBuf>> {
+ pub fn values(&self) -> BTreeMapValuesIter<'_, OutputType, Option<OutFileName>> {
self.0.values()
}
@@ -606,10 +737,18 @@ pub enum PrintRequest {
pub enum TraitSolver {
/// Classic trait solver in `rustc_trait_selection::traits::select`
Classic,
- /// Chalk trait solver
- Chalk,
/// Experimental trait solver in `rustc_trait_selection::solve`
Next,
+ /// Use the new trait solver during coherence
+ NextCoherence,
+}
+
+#[derive(Default, Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum DumpSolverProofTree {
+ Always,
+ OnError,
+ #[default]
+ Never,
}
pub enum Input {
@@ -658,11 +797,71 @@ impl Input {
}
}
+#[derive(Clone, Hash, Debug, HashStable_Generic, PartialEq)]
+pub enum OutFileName {
+ Real(PathBuf),
+ Stdout,
+}
+
+impl OutFileName {
+ pub fn parent(&self) -> Option<&Path> {
+ match *self {
+ OutFileName::Real(ref path) => path.parent(),
+ OutFileName::Stdout => None,
+ }
+ }
+
+ pub fn filestem(&self) -> Option<&OsStr> {
+ match *self {
+ OutFileName::Real(ref path) => path.file_stem(),
+ OutFileName::Stdout => Some(OsStr::new("stdout")),
+ }
+ }
+
+ pub fn is_stdout(&self) -> bool {
+ match *self {
+ OutFileName::Real(_) => false,
+ OutFileName::Stdout => true,
+ }
+ }
+
+ pub fn is_tty(&self) -> bool {
+ match *self {
+ OutFileName::Real(_) => false,
+ OutFileName::Stdout => atty::is(atty::Stream::Stdout),
+ }
+ }
+
+ pub fn as_path(&self) -> &Path {
+ match *self {
+ OutFileName::Real(ref path) => path.as_ref(),
+ OutFileName::Stdout => &Path::new("stdout"),
+ }
+ }
+
+ /// For a given output filename, return the actual name of the file that
+ /// can be used to write codegen data of type `flavor`. For real-path
+ /// output filenames, this would be trivial as we can just use the path.
+ /// Otherwise for stdout, return a temporary path so that the codegen data
+ /// may be later copied to stdout.
+ pub fn file_for_writing(
+ &self,
+ outputs: &OutputFilenames,
+ flavor: OutputType,
+ codegen_unit_name: Option<&str>,
+ ) -> PathBuf {
+ match *self {
+ OutFileName::Real(ref path) => path.clone(),
+ OutFileName::Stdout => outputs.temp_path(flavor, codegen_unit_name),
+ }
+ }
+}
+
#[derive(Clone, Hash, Debug, HashStable_Generic)]
pub struct OutputFilenames {
pub out_directory: PathBuf,
filestem: String,
- pub single_output_file: Option<PathBuf>,
+ pub single_output_file: Option<OutFileName>,
pub temps_directory: Option<PathBuf>,
pub outputs: OutputTypes,
}
@@ -675,7 +874,7 @@ impl OutputFilenames {
pub fn new(
out_directory: PathBuf,
out_filestem: String,
- single_output_file: Option<PathBuf>,
+ single_output_file: Option<OutFileName>,
temps_directory: Option<PathBuf>,
extra: String,
outputs: OutputTypes,
@@ -689,12 +888,12 @@ impl OutputFilenames {
}
}
- pub fn path(&self, flavor: OutputType) -> PathBuf {
+ pub fn path(&self, flavor: OutputType) -> OutFileName {
self.outputs
.get(&flavor)
.and_then(|p| p.to_owned())
.or_else(|| self.single_output_file.clone())
- .unwrap_or_else(|| self.output_path(flavor))
+ .unwrap_or_else(|| OutFileName::Real(self.output_path(flavor)))
}
/// Gets the output path where a compilation artifact of the given type
@@ -828,6 +1027,7 @@ impl Default for Options {
json_future_incompat: false,
pretty: None,
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
+ color: ColorConfig::Auto,
}
}
}
@@ -1308,6 +1508,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo
}
pub(super) fn build_target_config(
+ handler: &EarlyErrorHandler,
opts: &Options,
target_override: Option<Target>,
sysroot: &Path,
@@ -1317,27 +1518,21 @@ pub(super) fn build_target_config(
|t| Ok((t, TargetWarnings::empty())),
);
let (target, target_warnings) = target_result.unwrap_or_else(|e| {
- early_error(
- opts.error_format,
- format!(
- "Error loading target specification: {}. \
+ handler.early_error(format!(
+ "Error loading target specification: {}. \
Run `rustc --print target-list` for a list of built-in targets",
- e
- ),
- )
+ e
+ ))
});
for warning in target_warnings.warning_messages() {
- early_warn(opts.error_format, warning)
+ handler.early_warn(warning)
}
if !matches!(target.pointer_width, 16 | 32 | 64) {
- early_error(
- opts.error_format,
- format!(
- "target specification was invalid: unrecognized target-pointer-width {}",
- target.pointer_width
- ),
- )
+ handler.early_error(format!(
+ "target specification was invalid: unrecognized target-pointer-width {}",
+ target.pointer_width
+ ))
}
target
@@ -1573,8 +1768,8 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
}
pub fn get_cmd_lint_options(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
let mut lint_opts_with_position = vec![];
let mut describe_lints = false;
@@ -1598,14 +1793,14 @@ pub fn get_cmd_lint_options(
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
lint::Level::from_str(&cap)
- .unwrap_or_else(|| early_error(error_format, format!("unknown lint level: `{cap}`")))
+ .unwrap_or_else(|| handler.early_error(format!("unknown lint level: `{cap}`")))
});
(lint_opts, describe_lints, lint_cap)
}
/// Parses the `--color` flag.
-pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
+pub fn parse_color(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> ColorConfig {
match matches.opt_str("color").as_deref() {
Some("auto") => ColorConfig::Auto,
Some("always") => ColorConfig::Always,
@@ -1613,13 +1808,10 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
None => ColorConfig::Auto,
- Some(arg) => early_error(
- ErrorOutputType::default(),
- format!(
- "argument for `--color` must be auto, \
+ Some(arg) => handler.early_error(format!(
+ "argument for `--color` must be auto, \
always or never (instead was `{arg}`)"
- ),
- ),
+ )),
}
}
@@ -1662,7 +1854,7 @@ impl JsonUnusedExterns {
///
/// The first value returned is how to render JSON diagnostics, and the second
/// is whether or not artifact notifications are enabled.
-pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
+pub fn parse_json(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> JsonConfig {
let mut json_rendered: fn(ColorConfig) -> HumanReadableErrorType =
HumanReadableErrorType::Default;
let mut json_color = ColorConfig::Never;
@@ -1674,10 +1866,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
// won't actually be emitting any colors and anything colorized is
// embedded in a diagnostic message anyway.
if matches.opt_str("color").is_some() {
- early_error(
- ErrorOutputType::default(),
- "cannot specify the `--color` option with `--json`",
- );
+ handler.early_error("cannot specify the `--color` option with `--json`");
}
for sub_option in option.split(',') {
@@ -1688,10 +1877,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
"unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
"unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
"future-incompat" => json_future_incompat = true,
- s => early_error(
- ErrorOutputType::default(),
- format!("unknown `--json` option `{s}`"),
- ),
+ s => handler.early_error(format!("unknown `--json` option `{s}`")),
}
}
}
@@ -1706,6 +1892,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
/// Parses the `--error-format` flag.
pub fn parse_error_format(
+ handler: &mut EarlyErrorHandler,
matches: &getopts::Matches,
color: ColorConfig,
json_rendered: HumanReadableErrorType,
@@ -1726,13 +1913,15 @@ pub fn parse_error_format(
Some("pretty-json") => ErrorOutputType::Json { pretty: true, json_rendered },
Some("short") => ErrorOutputType::HumanReadable(HumanReadableErrorType::Short(color)),
- Some(arg) => early_error(
- ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color)),
- format!(
+ Some(arg) => {
+ handler.abort_if_error_and_set_error_format(ErrorOutputType::HumanReadable(
+ HumanReadableErrorType::Default(color),
+ ));
+ handler.early_error(format!(
"argument for `--error-format` must be `human`, `json` or \
`short` (instead was `{arg}`)"
- ),
- ),
+ ))
+ }
}
} else {
ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(color))
@@ -1745,10 +1934,7 @@ pub fn parse_error_format(
// `--error-format=json`. This means that `--json` is specified we
// should actually be emitting JSON blobs.
_ if !matches.opt_strs("json").is_empty() => {
- early_error(
- ErrorOutputType::default(),
- "using `--json` requires also using `--error-format=json`",
- );
+ handler.early_error("using `--json` requires also using `--error-format=json`");
}
_ => {}
@@ -1757,16 +1943,13 @@ pub fn parse_error_format(
error_format
}
-pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
+pub fn parse_crate_edition(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Edition {
let edition = match matches.opt_str("edition") {
Some(arg) => Edition::from_str(&arg).unwrap_or_else(|_| {
- early_error(
- ErrorOutputType::default(),
- format!(
- "argument for `--edition` must be one of: \
+ handler.early_error(format!(
+ "argument for `--edition` must be one of: \
{EDITION_NAME_LIST}. (instead was `{arg}`)"
- ),
- )
+ ))
}),
None => DEFAULT_EDITION,
};
@@ -1781,39 +1964,42 @@ pub fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
} else {
format!("edition {edition} is unstable and only available with -Z unstable-options")
};
- early_error(ErrorOutputType::default(), msg)
+ handler.early_error(msg)
}
edition
}
fn check_error_format_stability(
+ handler: &mut EarlyErrorHandler,
unstable_opts: &UnstableOptions,
error_format: ErrorOutputType,
json_rendered: HumanReadableErrorType,
) {
if !unstable_opts.unstable_options {
if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
- early_error(
- ErrorOutputType::Json { pretty: false, json_rendered },
- "`--error-format=pretty-json` is unstable",
- );
+ handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
+ pretty: false,
+ json_rendered,
+ });
+ handler.early_error("`--error-format=pretty-json` is unstable");
}
if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
error_format
{
- early_error(
- ErrorOutputType::Json { pretty: false, json_rendered },
- "`--error-format=human-annotate-rs` is unstable",
- );
+ handler.abort_if_error_and_set_error_format(ErrorOutputType::Json {
+ pretty: false,
+ json_rendered,
+ });
+ handler.early_error("`--error-format=human-annotate-rs` is unstable");
}
}
}
fn parse_output_types(
+ handler: &EarlyErrorHandler,
unstable_opts: &UnstableOptions,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
) -> OutputTypes {
let mut output_types = BTreeMap::new();
if !unstable_opts.parse_only {
@@ -1821,16 +2007,16 @@ fn parse_output_types(
for output_type in list.split(',') {
let (shorthand, path) = match output_type.split_once('=') {
None => (output_type, None),
- Some((shorthand, path)) => (shorthand, Some(PathBuf::from(path))),
+ Some((shorthand, "-")) => (shorthand, Some(OutFileName::Stdout)),
+ Some((shorthand, path)) => {
+ (shorthand, Some(OutFileName::Real(PathBuf::from(path))))
+ }
};
let output_type = OutputType::from_shorthand(shorthand).unwrap_or_else(|| {
- early_error(
- error_format,
- format!(
- "unknown emission type: `{shorthand}` - expected one of: {display}",
- display = OutputType::shorthands_display(),
- ),
- )
+ handler.early_error(format!(
+ "unknown emission type: `{shorthand}` - expected one of: {display}",
+ display = OutputType::shorthands_display(),
+ ))
});
output_types.insert(output_type, path);
}
@@ -1843,9 +2029,9 @@ fn parse_output_types(
}
fn should_override_cgus_and_disable_thinlto(
+ handler: &EarlyErrorHandler,
output_types: &OutputTypes,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
mut codegen_units: Option<usize>,
) -> (bool, Option<usize>) {
let mut disable_local_thinlto = false;
@@ -1863,15 +2049,12 @@ fn should_override_cgus_and_disable_thinlto(
Some(n) if n > 1 => {
if matches.opt_present("o") {
for ot in &incompatible {
- early_warn(
- error_format,
- format!(
- "`--emit={ot}` with `-o` incompatible with \
+ handler.early_warn(format!(
+ "`--emit={ot}` with `-o` incompatible with \
`-C codegen-units=N` for N > 1",
- ),
- );
+ ));
}
- early_warn(error_format, "resetting to default -C codegen-units=1");
+ handler.early_warn("resetting to default -C codegen-units=1");
codegen_units = Some(1);
disable_local_thinlto = true;
}
@@ -1884,27 +2067,27 @@ fn should_override_cgus_and_disable_thinlto(
}
if codegen_units == Some(0) {
- early_error(error_format, "value for codegen units must be a positive non-zero integer");
+ handler.early_error("value for codegen units must be a positive non-zero integer");
}
(disable_local_thinlto, codegen_units)
}
-fn check_thread_count(unstable_opts: &UnstableOptions, error_format: ErrorOutputType) {
+fn check_thread_count(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) {
if unstable_opts.threads == 0 {
- early_error(error_format, "value for threads must be a positive non-zero integer");
+ handler.early_error("value for threads must be a positive non-zero integer");
}
if unstable_opts.threads > 1 && unstable_opts.fuel.is_some() {
- early_error(error_format, "optimization fuel is incompatible with multiple threads");
+ handler.early_error("optimization fuel is incompatible with multiple threads");
}
}
fn collect_print_requests(
+ handler: &EarlyErrorHandler,
cg: &mut CodegenOptions,
unstable_opts: &mut UnstableOptions,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
) -> Vec<PrintRequest> {
let mut prints = Vec::<PrintRequest>::new();
if cg.target_cpu.as_ref().is_some_and(|s| s == "help") {
@@ -1944,8 +2127,7 @@ fn collect_print_requests(
if unstable_opts.unstable_options {
PrintRequest::TargetSpec
} else {
- early_error(
- error_format,
+ handler.early_error(
"the `-Z unstable-options` flag must also be passed to \
enable the target-spec-json print option",
);
@@ -1955,8 +2137,7 @@ fn collect_print_requests(
if unstable_opts.unstable_options {
PrintRequest::AllTargetSpecs
} else {
- early_error(
- error_format,
+ handler.early_error(
"the `-Z unstable-options` flag must also be passed to \
enable the all-target-specs-json print option",
);
@@ -1967,10 +2148,9 @@ fn collect_print_requests(
let prints =
PRINT_REQUESTS.iter().map(|(name, _)| format!("`{name}`")).collect::<Vec<_>>();
let prints = prints.join(", ");
- early_error(
- error_format,
- format!("unknown print request `{req}`. Valid print requests are: {prints}"),
- );
+ handler.early_error(format!(
+ "unknown print request `{req}`. Valid print requests are: {prints}"
+ ));
}
}
}));
@@ -1979,14 +2159,14 @@ fn collect_print_requests(
}
pub fn parse_target_triple(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
) -> TargetTriple {
match matches.opt_str("target") {
Some(target) if target.ends_with(".json") => {
let path = Path::new(&target);
TargetTriple::from_path(path).unwrap_or_else(|_| {
- early_error(error_format, format!("target file {path:?} does not exist"))
+ handler.early_error(format!("target file {path:?} does not exist"))
})
}
Some(target) => TargetTriple::TargetTriple(target),
@@ -1995,9 +2175,9 @@ pub fn parse_target_triple(
}
fn parse_opt_level(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
cg: &CodegenOptions,
- error_format: ErrorOutputType,
) -> OptLevel {
// The `-O` and `-C opt-level` flags specify the same setting, so we want to be able
// to use them interchangeably. However, because they're technically different flags,
@@ -2025,13 +2205,10 @@ fn parse_opt_level(
"s" => OptLevel::Size,
"z" => OptLevel::SizeMin,
arg => {
- early_error(
- error_format,
- format!(
- "optimization level needs to be \
+ handler.early_error(format!(
+ "optimization level needs to be \
between 0-3, s or z (instead was `{arg}`)"
- ),
- );
+ ));
}
}
}
@@ -2051,23 +2228,23 @@ fn select_debuginfo(matches: &getopts::Matches, cg: &CodegenOptions) -> DebugInf
}
pub(crate) fn parse_assert_incr_state(
+ handler: &EarlyErrorHandler,
opt_assertion: &Option<String>,
- error_format: ErrorOutputType,
) -> Option<IncrementalStateAssertion> {
match opt_assertion {
Some(s) if s.as_str() == "loaded" => Some(IncrementalStateAssertion::Loaded),
Some(s) if s.as_str() == "not-loaded" => Some(IncrementalStateAssertion::NotLoaded),
Some(s) => {
- early_error(error_format, format!("unexpected incremental state assertion value: {s}"))
+ handler.early_error(format!("unexpected incremental state assertion value: {s}"))
}
None => None,
}
}
fn parse_native_lib_kind(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
kind: &str,
- error_format: ErrorOutputType,
) -> (NativeLibKind, Option<bool>) {
let (kind, modifiers) = match kind.split_once(':') {
None => (kind, None),
@@ -2085,35 +2262,31 @@ fn parse_native_lib_kind(
} else {
", the `-Z unstable-options` flag must also be passed to use it"
};
- early_error(error_format, format!("library kind `link-arg` is unstable{why}"))
+ handler.early_error(format!("library kind `link-arg` is unstable{why}"))
}
NativeLibKind::LinkArg
}
- _ => early_error(
- error_format,
- format!(
- "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
- ),
- ),
+ _ => handler.early_error(format!(
+ "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
+ )),
};
match modifiers {
None => (kind, None),
- Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches),
+ Some(modifiers) => parse_native_lib_modifiers(handler, kind, modifiers, matches),
}
}
fn parse_native_lib_modifiers(
+ handler: &EarlyErrorHandler,
mut kind: NativeLibKind,
modifiers: &str,
- error_format: ErrorOutputType,
matches: &getopts::Matches,
) -> (NativeLibKind, Option<bool>) {
let mut verbatim = None;
for modifier in modifiers.split(',') {
let (modifier, value) = match modifier.strip_prefix(['+', '-']) {
Some(m) => (m, modifier.starts_with('+')),
- None => early_error(
- error_format,
+ None => handler.early_error(
"invalid linking modifier syntax, expected '+' or '-' prefix \
before one of: bundle, verbatim, whole-archive, as-needed",
),
@@ -2126,21 +2299,20 @@ fn parse_native_lib_modifiers(
} else {
", the `-Z unstable-options` flag must also be passed to use it"
};
- early_error(error_format, format!("linking modifier `{modifier}` is unstable{why}"))
+ handler.early_error(format!("linking modifier `{modifier}` is unstable{why}"))
}
};
let assign_modifier = |dst: &mut Option<bool>| {
if dst.is_some() {
let msg = format!("multiple `{modifier}` modifiers in a single `-l` option");
- early_error(error_format, msg)
+ handler.early_error(msg)
} else {
*dst = Some(value);
}
};
match (modifier, &mut kind) {
("bundle", NativeLibKind::Static { bundle, .. }) => assign_modifier(bundle),
- ("bundle", _) => early_error(
- error_format,
+ ("bundle", _) => handler.early_error(
"linking modifier `bundle` is only compatible with `static` linking kind",
),
@@ -2149,8 +2321,7 @@ fn parse_native_lib_modifiers(
("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
assign_modifier(whole_archive)
}
- ("whole-archive", _) => early_error(
- error_format,
+ ("whole-archive", _) => handler.early_error(
"linking modifier `whole-archive` is only compatible with `static` linking kind",
),
@@ -2159,28 +2330,24 @@ fn parse_native_lib_modifiers(
report_unstable_modifier();
assign_modifier(as_needed)
}
- ("as-needed", _) => early_error(
- error_format,
+ ("as-needed", _) => handler.early_error(
"linking modifier `as-needed` is only compatible with \
`dylib` and `framework` linking kinds",
),
// Note: this error also excludes the case with empty modifier
// string, like `modifiers = ""`.
- _ => early_error(
- error_format,
- format!(
- "unknown linking modifier `{modifier}`, expected one \
+ _ => handler.early_error(format!(
+ "unknown linking modifier `{modifier}`, expected one \
of: bundle, verbatim, whole-archive, as-needed"
- ),
- ),
+ )),
}
}
(kind, verbatim)
}
-fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> {
+fn parse_libs(handler: &EarlyErrorHandler, matches: &getopts::Matches) -> Vec<NativeLib> {
matches
.opt_strs("l")
.into_iter()
@@ -2194,7 +2361,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
let (name, kind, verbatim) = match s.split_once('=') {
None => (s, NativeLibKind::Unspecified, None),
Some((kind, name)) => {
- let (kind, verbatim) = parse_native_lib_kind(matches, kind, error_format);
+ let (kind, verbatim) = parse_native_lib_kind(handler, matches, kind);
(name.to_string(), kind, verbatim)
}
};
@@ -2204,7 +2371,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
};
if name.is_empty() {
- early_error(error_format, "library name must not be empty");
+ handler.early_error("library name must not be empty");
}
NativeLib { name, new_name, kind, verbatim }
})
@@ -2212,9 +2379,9 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
}
pub fn parse_externs(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
unstable_opts: &UnstableOptions,
- error_format: ErrorOutputType,
) -> Externs {
let is_unstable_enabled = unstable_opts.unstable_options;
let mut externs: BTreeMap<String, ExternEntry> = BTreeMap::new();
@@ -2278,8 +2445,7 @@ pub fn parse_externs(
let mut force = false;
if let Some(opts) = options {
if !is_unstable_enabled {
- early_error(
- error_format,
+ handler.early_error(
"the `-Z unstable-options` flag must also be passed to \
enable `--extern` options",
);
@@ -2291,15 +2457,14 @@ pub fn parse_externs(
if let ExternLocation::ExactPaths(_) = &entry.location {
add_prelude = false;
} else {
- early_error(
- error_format,
+ handler.early_error(
"the `noprelude` --extern option requires a file path",
);
}
}
"nounused" => nounused_dep = true,
"force" => force = true,
- _ => early_error(error_format, format!("unknown --extern option `{opt}`")),
+ _ => handler.early_error(format!("unknown --extern option `{opt}`")),
}
}
}
@@ -2318,18 +2483,15 @@ pub fn parse_externs(
}
fn parse_remap_path_prefix(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
unstable_opts: &UnstableOptions,
- error_format: ErrorOutputType,
) -> Vec<(PathBuf, PathBuf)> {
let mut mapping: Vec<(PathBuf, PathBuf)> = matches
.opt_strs("remap-path-prefix")
.into_iter()
.map(|remap| match remap.rsplit_once('=') {
- None => early_error(
- error_format,
- "--remap-path-prefix must contain '=' between FROM and TO",
- ),
+ None => handler.early_error("--remap-path-prefix must contain '=' between FROM and TO"),
Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
})
.collect();
@@ -2345,86 +2507,75 @@ fn parse_remap_path_prefix(
// JUSTIFICATION: before wrapper fn is available
#[allow(rustc::bad_opt_access)]
-pub fn build_session_options(matches: &getopts::Matches) -> Options {
- let color = parse_color(matches);
+pub fn build_session_options(
+ handler: &mut EarlyErrorHandler,
+ matches: &getopts::Matches,
+) -> Options {
+ let color = parse_color(handler, matches);
- let edition = parse_crate_edition(matches);
+ let edition = parse_crate_edition(handler, matches);
let JsonConfig {
json_rendered,
json_artifact_notifications,
json_unused_externs,
json_future_incompat,
- } = parse_json(matches);
+ } = parse_json(handler, matches);
- let error_format = parse_error_format(matches, color, json_rendered);
+ let error_format = parse_error_format(handler, matches, color, json_rendered);
let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| {
- early_error(error_format, "`--diagnostic-width` must be an positive integer");
+ handler.early_error("`--diagnostic-width` must be an positive integer");
});
let unparsed_crate_types = matches.opt_strs("crate-type");
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
- .unwrap_or_else(|e| early_error(error_format, e));
+ .unwrap_or_else(|e| handler.early_error(e));
- let mut unstable_opts = UnstableOptions::build(matches, error_format);
- let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
+ let mut unstable_opts = UnstableOptions::build(handler, matches);
+ let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(handler, matches);
- check_error_format_stability(&unstable_opts, error_format, json_rendered);
+ check_error_format_stability(handler, &unstable_opts, error_format, json_rendered);
if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
- early_error(
- error_format,
+ handler.early_error(
"the `-Z unstable-options` flag must also be passed to enable \
the flag `--json=unused-externs`",
);
}
- let output_types = parse_output_types(&unstable_opts, matches, error_format);
+ let output_types = parse_output_types(handler, &unstable_opts, matches);
- let mut cg = CodegenOptions::build(matches, error_format);
- let (disable_local_thinlto, mut codegen_units) = should_override_cgus_and_disable_thinlto(
- &output_types,
- matches,
- error_format,
- cg.codegen_units,
- );
+ let mut cg = CodegenOptions::build(handler, matches);
+ let (disable_local_thinlto, mut codegen_units) =
+ should_override_cgus_and_disable_thinlto(handler, &output_types, matches, cg.codegen_units);
- check_thread_count(&unstable_opts, error_format);
+ check_thread_count(handler, &unstable_opts);
let incremental = cg.incremental.as_ref().map(PathBuf::from);
- let assert_incr_state = parse_assert_incr_state(&unstable_opts.assert_incr_state, error_format);
+ let assert_incr_state = parse_assert_incr_state(handler, &unstable_opts.assert_incr_state);
if unstable_opts.profile && incremental.is_some() {
- early_error(
- error_format,
- "can't instrument with gcov profiling when compiling incrementally",
- );
+ handler.early_error("can't instrument with gcov profiling when compiling incrementally");
}
if unstable_opts.profile {
match codegen_units {
Some(1) => {}
None => codegen_units = Some(1),
- Some(_) => early_error(
- error_format,
- "can't instrument with gcov profiling with multiple codegen units",
- ),
+ Some(_) => handler
+ .early_error("can't instrument with gcov profiling with multiple codegen units"),
}
}
if cg.profile_generate.enabled() && cg.profile_use.is_some() {
- early_error(
- error_format,
- "options `-C profile-generate` and `-C profile-use` are exclusive",
- );
+ handler.early_error("options `-C profile-generate` and `-C profile-use` are exclusive");
}
if unstable_opts.profile_sample_use.is_some()
&& (cg.profile_generate.enabled() || cg.profile_use.is_some())
{
- early_error(
- error_format,
+ handler.early_error(
"option `-Z profile-sample-use` cannot be used with `-C profile-generate` or `-C profile-use`",
);
}
@@ -2433,23 +2584,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
// precedence.
match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
(Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
- early_error(
- error_format,
+ handler.early_error(
"incompatible values passed for `-C symbol-mangling-version` \
and `-Z symbol-mangling-version`",
);
}
(Some(SymbolManglingVersion::V0), _) => {}
(Some(_), _) if !unstable_opts.unstable_options => {
- early_error(
- error_format,
- "`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
- );
+ handler
+ .early_error("`-C symbol-mangling-version=legacy` requires `-Z unstable-options`");
}
(None, None) => {}
(None, smv) => {
- early_warn(
- error_format,
+ handler.early_warn(
"`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
);
cg.symbol_mangling_version = smv;
@@ -2461,25 +2608,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
// precedence.
match (cg.instrument_coverage, unstable_opts.instrument_coverage) {
(Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
- early_error(
- error_format,
+ handler.early_error(
"incompatible values passed for `-C instrument-coverage` \
and `-Z instrument-coverage`",
);
}
(Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
(Some(_), _) if !unstable_opts.unstable_options => {
- early_error(
- error_format,
- "`-C instrument-coverage=except-*` requires `-Z unstable-options`",
- );
+ handler.early_error("`-C instrument-coverage=except-*` requires `-Z unstable-options`");
}
(None, None) => {}
(None, ic) => {
- early_warn(
- error_format,
- "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`",
- );
+ handler
+ .early_warn("`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`");
cg.instrument_coverage = ic;
}
_ => {}
@@ -2487,8 +2628,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
if cg.profile_generate.enabled() || cg.profile_use.is_some() {
- early_error(
- error_format,
+ handler.early_error(
"option `-C instrument-coverage` is not compatible with either `-C profile-use` \
or `-C profile-generate`",
);
@@ -2501,8 +2641,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
match cg.symbol_mangling_version {
None => cg.symbol_mangling_version = Some(SymbolManglingVersion::V0),
Some(SymbolManglingVersion::Legacy) => {
- early_warn(
- error_format,
+ handler.early_warn(
"-C instrument-coverage requires symbol mangling version `v0`, \
but `-C symbol-mangling-version=legacy` was specified",
);
@@ -2518,20 +2657,44 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
if !cg.embed_bitcode {
match cg.lto {
LtoCli::No | LtoCli::Unspecified => {}
- LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => early_error(
- error_format,
- "options `-C embed-bitcode=no` and `-C lto` are incompatible",
- ),
+ LtoCli::Yes | LtoCli::NoParam | LtoCli::Thin | LtoCli::Fat => {
+ handler.early_error("options `-C embed-bitcode=no` and `-C lto` are incompatible")
+ }
+ }
+ }
+
+ // For testing purposes, until we have more feedback about these options: ensure `-Z
+ // unstable-options` is required when using the unstable `-C link-self-contained` options, like
+ // `-C link-self-contained=+linker`, and when using the unstable `-C linker-flavor` options, like
+ // `-C linker-flavor=gnu-lld-cc`.
+ if !nightly_options::is_unstable_enabled(matches) {
+ let uses_unstable_self_contained_option =
+ cg.link_self_contained.are_unstable_variants_set();
+ if uses_unstable_self_contained_option {
+ handler.early_error(
+ "only `-C link-self-contained` values `y`/`yes`/`on`/`n`/`no`/`off` are stable, \
+ the `-Z unstable-options` flag must also be passed to use the unstable values",
+ );
+ }
+
+ if let Some(flavor) = cg.linker_flavor {
+ if flavor.is_unstable() {
+ handler.early_error(format!(
+ "the linker flavor `{}` is unstable, the `-Z unstable-options` \
+ flag must also be passed to use the unstable values",
+ flavor.desc()
+ ));
+ }
}
}
- let prints = collect_print_requests(&mut cg, &mut unstable_opts, matches, error_format);
+ let prints = collect_print_requests(handler, &mut cg, &mut unstable_opts, matches);
let cg = cg;
let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
- let target_triple = parse_target_triple(matches, error_format);
- let opt_level = parse_opt_level(matches, &cg, error_format);
+ let target_triple = parse_target_triple(handler, matches);
+ let opt_level = parse_opt_level(handler, matches, &cg);
// The `-g` and `-C debuginfo` flags specify the same setting, so we want to be able
// to use them interchangeably. See the note above (regarding `-O` and `-C opt-level`)
// for more details.
@@ -2540,28 +2703,32 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let mut search_paths = vec![];
for s in &matches.opt_strs("L") {
- search_paths.push(SearchPath::from_cli_opt(s, error_format));
+ search_paths.push(SearchPath::from_cli_opt(handler, s));
}
- let libs = parse_libs(matches, error_format);
+ let libs = parse_libs(handler, matches);
let test = matches.opt_present("test");
if !cg.remark.is_empty() && debuginfo == DebugInfo::None {
- early_warn(error_format, "-C remark requires \"-C debuginfo=n\" to show source locations");
+ handler.early_warn("-C remark requires \"-C debuginfo=n\" to show source locations");
}
- let externs = parse_externs(matches, &unstable_opts, error_format);
+ if cg.remark.is_empty() && unstable_opts.remark_dir.is_some() {
+ handler.early_warn("using -Z remark-dir without enabling remarks using e.g. -C remark=all");
+ }
+
+ let externs = parse_externs(handler, matches, &unstable_opts);
let crate_name = matches.opt_str("crate-name");
- let remap_path_prefix = parse_remap_path_prefix(matches, &unstable_opts, error_format);
+ let remap_path_prefix = parse_remap_path_prefix(handler, matches, &unstable_opts);
- let pretty = parse_pretty(&unstable_opts, error_format);
+ let pretty = parse_pretty(handler, &unstable_opts);
// query-dep-graph is required if dump-dep-graph is given #106736
if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
- early_error(error_format, "can't dump dependency graph without `-Z query-dep-graph`");
+ handler.early_error("can't dump dependency graph without `-Z query-dep-graph`");
}
// Try to find a directory containing the Rust `src`, for more details see
@@ -2593,7 +2760,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
};
let working_dir = std::env::current_dir().unwrap_or_else(|e| {
- early_error(error_format, format!("Current directory is invalid: {e}"));
+ handler.early_error(format!("Current directory is invalid: {e}"));
});
let remap = FilePathMapping::new(remap_path_prefix.clone());
@@ -2641,10 +2808,11 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
json_future_incompat,
pretty,
working_dir,
+ color,
}
}
-fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Option<PpMode> {
+fn parse_pretty(handler: &EarlyErrorHandler, unstable_opts: &UnstableOptions) -> Option<PpMode> {
use PpMode::*;
let first = match unstable_opts.unpretty.as_deref()? {
@@ -2663,16 +2831,13 @@ fn parse_pretty(unstable_opts: &UnstableOptions, efmt: ErrorOutputType) -> Optio
"thir-flat" => ThirFlat,
"mir" => Mir,
"mir-cfg" => MirCFG,
- name => early_error(
- efmt,
- format!(
- "argument to `unpretty` must be one of `normal`, `identified`, \
+ name => handler.early_error(format!(
+ "argument to `unpretty` must be one of `normal`, `identified`, \
`expanded`, `expanded,identified`, `expanded,hygiene`, \
`ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
`hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \
`mir-cfg`; got {name}"
- ),
- ),
+ )),
};
debug!("got unpretty option: {first:?}");
Some(first)
@@ -2712,8 +2877,8 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
}
pub mod nightly_options {
- use super::{ErrorOutputType, OptionStability, RustcOptGroup};
- use crate::early_error;
+ use super::{OptionStability, RustcOptGroup};
+ use crate::EarlyErrorHandler;
use rustc_feature::UnstableFeatures;
pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
@@ -2729,7 +2894,11 @@ pub mod nightly_options {
UnstableFeatures::from_environment(krate).is_nightly_build()
}
- pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
+ pub fn check_nightly_options(
+ handler: &EarlyErrorHandler,
+ matches: &getopts::Matches,
+ flags: &[RustcOptGroup],
+ ) {
let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
let really_allows_unstable_options = match_is_nightly_build(matches);
@@ -2741,14 +2910,11 @@ pub mod nightly_options {
continue;
}
if opt.name != "Z" && !has_z_unstable_option {
- early_error(
- ErrorOutputType::default(),
- format!(
- "the `-Z unstable-options` flag must also be passed to enable \
+ handler.early_error(format!(
+ "the `-Z unstable-options` flag must also be passed to enable \
the flag `{}`",
- opt.name
- ),
- );
+ opt.name
+ ));
}
if really_allows_unstable_options {
continue;
@@ -2759,7 +2925,12 @@ pub mod nightly_options {
"the option `{}` is only accepted on the nightly compiler",
opt.name
);
- early_error(ErrorOutputType::default(), msg);
+ let _ = handler.early_error_no_abort(msg);
+ handler.early_note("selecting a toolchain with `+toolchain` arguments require a rustup proxy; see <https://rust-lang.github.io/rustup/concepts/index.html>");
+ handler.early_help(
+ "consider switching to a nightly toolchain: `rustup default nightly`",
+ );
+ handler.early_note("for more information about Rust's stability policy, see <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html#unstable-features>");
}
OptionStability::Stable => {}
}
@@ -2892,7 +3063,7 @@ pub(crate) mod dep_tracking {
use super::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli,
- OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks,
+ OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Passes, ResolveDocLinks,
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
TraitSolver, TrimmedDefPaths,
};
@@ -2990,6 +3161,7 @@ pub(crate) mod dep_tracking {
SourceFileHashAlgorithm,
TrimmedDefPaths,
Option<LdImpl>,
+ OutFileName,
OutputType,
RealFileName,
LocationDetail,
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 546c0fa8e..4a3e668da 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -422,3 +422,11 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span:
pub struct OptimisationFuelExhausted {
pub msg: String,
}
+
+#[derive(Diagnostic)]
+#[diag(session_incompatible_linker_flavor)]
+#[note]
+pub struct IncompatibleLinkerFlavor {
+ pub flavor: &'static str,
+ pub compatible_list: String,
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 590a68c66..d57aa820f 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -27,7 +27,7 @@ pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pas
pub use rustc_lint_defs as lint;
pub mod parse;
-mod code_stats;
+pub mod code_stats;
#[macro_use]
pub mod config;
pub mod cstore;
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 2c4c4a7a6..7840a0ecf 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -1,10 +1,10 @@
use crate::config::*;
-use crate::early_error;
-use crate::lint;
use crate::search_paths::SearchPath;
use crate::utils::NativeLib;
+use crate::{lint, EarlyErrorHandler};
use rustc_data_structures::profiling::TimePassesFormat;
+use rustc_errors::ColorConfig;
use rustc_errors::{LanguageIdentifier, TerminalUrl};
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
use rustc_target::spec::{
@@ -213,6 +213,7 @@ top_level_options!(
/// The (potentially remapped) working directory
working_dir: RealFileName [TRACKED],
+ color: ColorConfig [UNTRACKED],
}
);
@@ -245,10 +246,10 @@ macro_rules! options {
impl $struct_name {
pub fn build(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
- error_format: ErrorOutputType,
) -> $struct_name {
- build_options(matches, $stat, $prefix, $outputname, error_format)
+ build_options(handler, matches, $stat, $prefix, $outputname)
}
fn dep_tracking_hash(&self, for_crate_hash: bool, error_format: ErrorOutputType) -> u64 {
@@ -309,11 +310,11 @@ type OptionSetter<O> = fn(&mut O, v: Option<&str>) -> bool;
type OptionDescrs<O> = &'static [(&'static str, OptionSetter<O>, &'static str, &'static str)];
fn build_options<O: Default>(
+ handler: &EarlyErrorHandler,
matches: &getopts::Matches,
descrs: OptionDescrs<O>,
prefix: &str,
outputname: &str,
- error_format: ErrorOutputType,
) -> O {
let mut op = O::default();
for option in matches.opt_strs(prefix) {
@@ -327,15 +328,13 @@ fn build_options<O: Default>(
Some((_, setter, type_desc, _)) => {
if !setter(&mut op, value) {
match value {
- None => early_error(
- error_format,
+ None => handler.early_error(
format!(
"{0} option `{1}` requires {2} ({3} {1}=<value>)",
outputname, key, type_desc, prefix
),
),
- Some(value) => early_error(
- error_format,
+ Some(value) => handler.early_error(
format!(
"incorrect value `{value}` for {outputname} option `{key}` - {type_desc} was expected"
),
@@ -343,7 +342,7 @@ fn build_options<O: Default>(
}
}
}
- None => early_error(error_format, format!("unknown {outputname} option: `{key}`")),
+ None => handler.early_error(format!("unknown {outputname} option: `{key}`")),
}
}
return op;
@@ -372,7 +371,7 @@ mod desc {
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
- pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `shadow-call-stack`, or `thread`";
+ pub const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `hwaddress`, `kcfi`, `kernel-address`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, or `thread`";
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
@@ -389,7 +388,7 @@ mod desc {
pub const parse_unpretty: &str = "`string` or `string=string`";
pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0";
pub const parse_trait_solver: &str =
- "one of the supported solver modes (`classic`, `chalk`, or `next`)";
+ "one of the supported solver modes (`classic`, `next`, or `next-coherence`)";
pub const parse_lto: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted";
pub const parse_linker_plugin_lto: &str =
@@ -413,12 +412,15 @@ mod desc {
pub const parse_split_dwarf_kind: &str =
"one of supported split dwarf modes (`split` or `single`)";
pub const parse_gcc_ld: &str = "one of: no value, `lld`";
+ pub const parse_link_self_contained: &str = "one of: `y`, `yes`, `on`, `n`, `no`, `off`, or a list of enabled (`+` prefix) and disabled (`-` prefix) \
+ components: `crto`, `libc`, `unwind`, `linker`, `sanitizers`, `mingw`";
pub const parse_stack_protector: &str =
"one of (`none` (default), `basic`, `strong`, or `all`)";
pub const parse_branch_protection: &str =
"a `,` separated combination of `bti`, `b-key`, `pac-ret`, or `leaf`";
pub const parse_proc_macro_execution_strategy: &str =
"one of supported execution strategies (`same-thread`, or `cross-thread`)";
+ pub const parse_dump_solver_proof_tree: &str = "one of: `always`, `on-request`, `on-error`";
}
mod parse {
@@ -694,6 +696,7 @@ mod parse {
"shadow-call-stack" => SanitizerSet::SHADOWCALLSTACK,
"thread" => SanitizerSet::THREAD,
"hwaddress" => SanitizerSet::HWADDRESS,
+ "safestack" => SanitizerSet::SAFESTACK,
_ => return false,
}
}
@@ -983,8 +986,8 @@ mod parse {
pub(crate) fn parse_trait_solver(slot: &mut TraitSolver, v: Option<&str>) -> bool {
match v {
Some("classic") => *slot = TraitSolver::Classic,
- Some("chalk") => *slot = TraitSolver::Chalk,
Some("next") => *slot = TraitSolver::Next,
+ Some("next-coherence") => *slot = TraitSolver::NextCoherence,
// default trait solver is subject to change..
Some("default") => *slot = TraitSolver::Classic,
_ => return false,
@@ -1123,6 +1126,34 @@ mod parse {
}
}
+ pub(crate) fn parse_link_self_contained(slot: &mut LinkSelfContained, v: Option<&str>) -> bool {
+ // Whenever `-C link-self-contained` is passed without a value, it's an opt-in
+ // just like `parse_opt_bool`, the historical value of this flag.
+ //
+ // 1. Parse historical single bool values
+ let s = v.unwrap_or("y");
+ match s {
+ "y" | "yes" | "on" => {
+ slot.set_all_explicitly(true);
+ return true;
+ }
+ "n" | "no" | "off" => {
+ slot.set_all_explicitly(false);
+ return true;
+ }
+ _ => {}
+ }
+
+ // 2. Parse a list of enabled and disabled components.
+ for comp in s.split(",") {
+ if slot.handle_cli_component(comp).is_err() {
+ return false;
+ }
+ }
+
+ true
+ }
+
pub(crate) fn parse_wasi_exec_model(slot: &mut Option<WasiExecModel>, v: Option<&str>) -> bool {
match v {
Some("command") => *slot = Some(WasiExecModel::Command),
@@ -1209,6 +1240,19 @@ mod parse {
};
true
}
+
+ pub(crate) fn parse_dump_solver_proof_tree(
+ slot: &mut DumpSolverProofTree,
+ v: Option<&str>,
+ ) -> bool {
+ match v {
+ None | Some("always") => *slot = DumpSolverProofTree::Always,
+ Some("never") => *slot = DumpSolverProofTree::Never,
+ Some("on-error") => *slot = DumpSolverProofTree::OnError,
+ _ => return false,
+ };
+ true
+ }
}
options! {
@@ -1266,9 +1310,9 @@ options! {
#[rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field")]
link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
"keep dead code at link time (useful for code coverage) (default: no)"),
- link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+ link_self_contained: LinkSelfContained = (LinkSelfContained::default(), parse_link_self_contained, [UNTRACKED],
"control whether to link Rust provided C objects/libraries or rely
- on C toolchain installed in the system"),
+ on a C toolchain or linker installed in the system"),
linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"system linker to link outputs with"),
linker_flavor: Option<LinkerFlavorCli> = (None, parse_linker_flavor, [UNTRACKED],
@@ -1315,7 +1359,7 @@ options! {
"control generation of position-independent code (PIC) \
(`rustc --print relocation-models` for details)"),
remark: Passes = (Passes::Some(Vec::new()), parse_passes, [UNTRACKED],
- "print remarks for these optimization passes (space separated, or \"all\")"),
+ "output remarks for these optimization passes (space separated, or \"all\")"),
rpath: bool = (false, parse_bool, [UNTRACKED],
"set rpath values in libs/exes (default: no)"),
save_temps: bool = (false, parse_bool, [UNTRACKED],
@@ -1371,8 +1415,6 @@ options! {
"set options for branch target identification and pointer authentication on AArch64"),
cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED],
"instrument control-flow architecture protection"),
- cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED],
- "the codegen unit partitioning strategy to use"),
codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED],
"the backend to use"),
combine_cgu: bool = (false, parse_bool, [TRACKED],
@@ -1436,6 +1478,11 @@ options! {
"output statistics about monomorphization collection"),
dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED],
"the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"),
+ dump_solver_proof_tree: DumpSolverProofTree = (DumpSolverProofTree::Never, parse_dump_solver_proof_tree, [UNTRACKED],
+ "dump a proof tree for every goal evaluated by the new trait solver. If the flag is specified without any options after it
+ then it defaults to `always`. If the flag is not specified at all it defaults to `on-request`."),
+ dump_solver_proof_tree_use_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
+ "determines whether dumped proof trees use the global cache"),
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
@@ -1558,14 +1605,14 @@ options! {
"use like `-Zmir-enable-passes=+DestinationPropagation,-InstSimplify`. Forces the specified passes to be \
enabled, overriding all other checks. Passes that are not specified are enabled or \
disabled by other flags as usual."),
+ mir_include_spans: bool = (false, parse_bool, [UNTRACKED],
+ "use line numbers relative to the function in mir pretty printing"),
mir_keep_place_mention: bool = (false, parse_bool, [TRACKED],
"keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"),
#[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")]
mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
"MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
- mir_pretty_relative_line_numbers: bool = (false, parse_bool, [UNTRACKED],
- "use line numbers relative to the function in mir pretty printing"),
move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
"the size at which the `large_assignments` lint starts to be emitted"),
mutable_noalias: bool = (true, parse_bool, [TRACKED],
@@ -1611,7 +1658,7 @@ options! {
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
"whether to use the PLT when calling into shared libraries;
only has effect for PIC code on systems with ELF binaries
- (default: PLT is disabled if full relro is enabled)"),
+ (default: PLT is disabled if full relro is enabled on x86_64)"),
polonius: bool = (false, parse_bool, [TRACKED],
"enable polonius-based borrow-checker (default: no)"),
polymorphize: bool = (false, parse_bool, [TRACKED],
@@ -1632,6 +1679,8 @@ options! {
"print the result of the monomorphization collection pass"),
print_type_sizes: bool = (false, parse_bool, [UNTRACKED],
"print layout information for each type encountered (default: no)"),
+ print_vtable_sizes: bool = (false, parse_bool, [UNTRACKED],
+ "print size comparison between old and new vtable layouts (default: no)"),
proc_macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
"show backtraces for panics during proc-macro execution (default: no)"),
proc_macro_execution_strategy: ProcMacroExecutionStrategy = (ProcMacroExecutionStrategy::SameThread,
@@ -1658,6 +1707,9 @@ options! {
"choose which RELRO level to use"),
remap_cwd_prefix: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
"remap paths under the current working directory to this path prefix"),
+ remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
+ "directory into which to write optimization remarks (if not specified, they will be \
+written to standard error output)"),
report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
"immediately print bugs registered with `delay_span_bug` (default: no)"),
sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index fdb9fae44..2088744bc 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -1,5 +1,5 @@
//! Related to out filenames of compilation (e.g. save analysis, binaries).
-use crate::config::{CrateType, Input, OutputFilenames, OutputType};
+use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
use crate::errors::{
CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable,
InvalidCharacterInCrateName,
@@ -8,14 +8,14 @@ use crate::Session;
use rustc_ast::{self as ast, attr};
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
-use std::path::{Path, PathBuf};
+use std::path::Path;
pub fn out_filename(
sess: &Session,
crate_type: CrateType,
outputs: &OutputFilenames,
crate_name: Symbol,
-) -> PathBuf {
+) -> OutFileName {
let default_filename = filename_for_input(sess, crate_type, crate_name, outputs);
let out_filename = outputs
.outputs
@@ -24,7 +24,9 @@ pub fn out_filename(
.or_else(|| outputs.single_output_file.clone())
.unwrap_or(default_filename);
- check_file_is_writeable(&out_filename, sess);
+ if let OutFileName::Real(ref path) = out_filename {
+ check_file_is_writeable(path, sess);
+ }
out_filename
}
@@ -112,7 +114,7 @@ pub fn filename_for_metadata(
sess: &Session,
crate_name: Symbol,
outputs: &OutputFilenames,
-) -> PathBuf {
+) -> OutFileName {
// If the command-line specified the path, use that directly.
if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) {
return out_filename.clone();
@@ -120,12 +122,13 @@ pub fn filename_for_metadata(
let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
- let out_filename = outputs
- .single_output_file
- .clone()
- .unwrap_or_else(|| outputs.out_directory.join(&format!("lib{libname}.rmeta")));
+ let out_filename = outputs.single_output_file.clone().unwrap_or_else(|| {
+ OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rmeta")))
+ });
- check_file_is_writeable(&out_filename, sess);
+ if let OutFileName::Real(ref path) = out_filename {
+ check_file_is_writeable(path, sess);
+ }
out_filename
}
@@ -135,23 +138,33 @@ pub fn filename_for_input(
crate_type: CrateType,
crate_name: Symbol,
outputs: &OutputFilenames,
-) -> PathBuf {
+) -> OutFileName {
let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
match crate_type {
- CrateType::Rlib => outputs.out_directory.join(&format!("lib{libname}.rlib")),
+ CrateType::Rlib => {
+ OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rlib")))
+ }
CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => {
let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix);
- outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))
+ OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
}
CrateType::Staticlib => {
let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix);
- outputs.out_directory.join(&format!("{prefix}{libname}{suffix}"))
+ OutFileName::Real(outputs.out_directory.join(&format!("{prefix}{libname}{suffix}")))
}
CrateType::Executable => {
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..]) }
+ if let OutFileName::Real(ref path) = out_filename {
+ if suffix.is_empty() {
+ out_filename
+ } else {
+ OutFileName::Real(path.with_extension(&suffix[1..]))
+ }
+ } else {
+ out_filename
+ }
}
}
}
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 7b396dde9..194f7201f 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -51,13 +51,6 @@ impl GatedSpans {
debug_assert_eq!(span, removed_span);
}
- /// Is the provided `feature` gate ungated currently?
- ///
- /// Using this is discouraged unless you have a really good reason to.
- pub fn is_ungated(&self, feature: Symbol) -> bool {
- self.spans.borrow().get(&feature).map_or(true, |spans| spans.is_empty())
- }
-
/// Prepend the given set of `spans` onto the set in `self`.
pub fn merge(&self, mut spans: FxHashMap<Symbol, Vec<Span>>) {
let mut inner = self.spans.borrow_mut();
@@ -84,6 +77,7 @@ impl SymbolGallery {
/// Construct a diagnostic for a language feature error due to the given `span`.
/// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`.
+#[track_caller]
pub fn feature_err(
sess: &ParseSess,
feature: Symbol,
@@ -123,7 +117,7 @@ pub fn feature_err_issue(
/// Construct a future incompatibility diagnostic for a feature gate.
///
/// This diagnostic is only a warning and *does not cause compilation to fail*.
-pub fn feature_warn(sess: &ParseSess, feature: Symbol, span: Span, explain: &str) {
+pub fn feature_warn(sess: &ParseSess, feature: Symbol, span: Span, explain: &'static str) {
feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
}
@@ -140,7 +134,7 @@ pub fn feature_warn_issue(
feature: Symbol,
span: Span,
issue: GateIssue,
- explain: &str,
+ explain: &'static str,
) {
let mut err = sess.span_diagnostic.struct_span_warn(span, explain);
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
diff --git a/compiler/rustc_session/src/search_paths.rs b/compiler/rustc_session/src/search_paths.rs
index 56a6b6f3b..07e78d176 100644
--- a/compiler/rustc_session/src/search_paths.rs
+++ b/compiler/rustc_session/src/search_paths.rs
@@ -1,5 +1,5 @@
use crate::filesearch::make_target_lib_path;
-use crate::{config, early_error};
+use crate::EarlyErrorHandler;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug)]
@@ -46,7 +46,7 @@ impl PathKind {
}
impl SearchPath {
- pub fn from_cli_opt(path: &str, output: config::ErrorOutputType) -> Self {
+ pub fn from_cli_opt(handler: &EarlyErrorHandler, path: &str) -> Self {
let (kind, path) = if let Some(stripped) = path.strip_prefix("native=") {
(PathKind::Native, stripped)
} else if let Some(stripped) = path.strip_prefix("crate=") {
@@ -61,7 +61,7 @@ impl SearchPath {
(PathKind::All, path)
};
if path.is_empty() {
- early_error(output, "empty search path given via `-L`");
+ handler.early_error("empty search path given via `-L`");
}
let dir = PathBuf::from(path);
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index bbe52dbce..5be122ffb 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,8 +1,10 @@
use crate::cgu_reuse_tracker::CguReuseTracker;
use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
-use crate::config::Input;
-use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
+use crate::config::{
+ self, CrateType, InstrumentCoverage, OptLevel, OutFileName, OutputType, SwitchWithOptPath,
+};
+use crate::config::{ErrorOutputType, Input};
use crate::errors;
use crate::parse::{add_feature_diagnostics, ParseSess};
use crate::search_paths::{PathKind, SearchPath};
@@ -23,7 +25,7 @@ use rustc_errors::json::JsonEmitter;
use rustc_errors::registry::Registry;
use rustc_errors::{
error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
- ErrorGuaranteed, FluentBundle, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
+ ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
TerminalUrl,
};
use rustc_macros::HashStable_Generic;
@@ -128,14 +130,12 @@ pub struct Limits {
pub move_size_limit: Limit,
/// The maximum length of types during monomorphization.
pub type_length_limit: Limit,
- /// The maximum blocks a const expression can evaluate.
- pub const_eval_limit: Limit,
}
pub struct CompilerIO {
pub input: Input,
pub output_dir: Option<PathBuf>,
- pub output_file: Option<PathBuf>,
+ pub output_file: Option<OutFileName>,
pub temps_dir: Option<PathBuf>,
}
@@ -234,6 +234,27 @@ pub enum MetadataKind {
Compressed,
}
+#[derive(Clone, Copy)]
+pub enum CodegenUnits {
+ /// Specified by the user. In this case we try fairly hard to produce the
+ /// number of CGUs requested.
+ User(usize),
+
+ /// A default value, i.e. not specified by the user. In this case we take
+ /// more liberties about CGU formation, e.g. avoid producing very small
+ /// CGUs.
+ Default(usize),
+}
+
+impl CodegenUnits {
+ pub fn as_usize(self) -> usize {
+ match self {
+ CodegenUnits::User(n) => n,
+ CodegenUnits::Default(n) => n,
+ }
+ }
+}
+
impl Session {
pub fn miri_unleashed_feature(&self, span: Span, feature_gate: Option<Symbol>) {
self.miri_unleashed_features.lock().push((span, feature_gate));
@@ -988,11 +1009,11 @@ impl Session {
self.edition().rust_2024()
}
- /// Returns `true` if we cannot skip the PLT for shared library calls.
+ /// Returns `true` if we should use the PLT for shared library calls.
pub fn needs_plt(&self) -> bool {
- // Check if the current target usually needs PLT to be enabled.
+ // Check if the current target usually wants PLT to be enabled.
// The user can use the command line flag to override it.
- let needs_plt = self.target.needs_plt;
+ let want_plt = self.target.plt_by_default;
let dbg_opts = &self.opts.unstable_opts;
@@ -1004,8 +1025,8 @@ impl Session {
let full_relro = RelroLevel::Full == relro_level;
// If user didn't explicitly forced us to use / skip the PLT,
- // then try to skip it where possible.
- dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
+ // then use it unless the target doesn't want it by default or the full relro forces it on.
+ dbg_opts.plt.unwrap_or(want_plt || !full_relro)
}
/// Checks if LLVM lifetime markers should be emitted.
@@ -1104,7 +1125,7 @@ impl Session {
// If there's only one codegen unit and LTO isn't enabled then there's
// no need for ThinLTO so just return false.
- if self.codegen_units() == 1 {
+ if self.codegen_units().as_usize() == 1 {
return config::Lto::No;
}
@@ -1206,19 +1227,19 @@ impl Session {
/// Returns the number of codegen units that should be used for this
/// compilation
- pub fn codegen_units(&self) -> usize {
+ pub fn codegen_units(&self) -> CodegenUnits {
if let Some(n) = self.opts.cli_forced_codegen_units {
- return n;
+ return CodegenUnits::User(n);
}
if let Some(n) = self.target.default_codegen_units {
- return n as usize;
+ return CodegenUnits::Default(n as usize);
}
// If incremental compilation is turned on, we default to a high number
// codegen units in order to reduce the "collateral damage" small
// changes cause.
if self.opts.incremental.is_some() {
- return 256;
+ return CodegenUnits::Default(256);
}
// Why is 16 codegen units the default all the time?
@@ -1271,7 +1292,7 @@ impl Session {
// As a result 16 was chosen here! Mostly because it was a power of 2
// and most benchmarks agreed it was roughly a local optimum. Not very
// scientific.
- 16
+ CodegenUnits::Default(16)
}
pub fn teach(&self, code: &DiagnosticId) -> bool {
@@ -1361,6 +1382,7 @@ fn default_emitter(
// JUSTIFICATION: literally session construction
#[allow(rustc::bad_opt_access)]
pub fn build_session(
+ handler: &EarlyErrorHandler,
sopts: config::Options,
io: CompilerIO,
bundle: Option<Lrc<rustc_errors::FluentBundle>>,
@@ -1387,13 +1409,12 @@ pub fn build_session(
None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"),
};
- let target_cfg = config::build_target_config(&sopts, target_override, &sysroot);
+ let target_cfg = config::build_target_config(handler, &sopts, target_override, &sysroot);
let host_triple = TargetTriple::from_triple(config::host_triple());
- let (host, target_warnings) = Target::search(&host_triple, &sysroot).unwrap_or_else(|e| {
- early_error(sopts.error_format, format!("Error loading host specification: {e}"))
- });
+ let (host, target_warnings) = Target::search(&host_triple, &sysroot)
+ .unwrap_or_else(|e| handler.early_error(format!("Error loading host specification: {e}")));
for warning in target_warnings.warning_messages() {
- early_warn(sopts.error_format, warning)
+ handler.early_warn(warning)
}
let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader));
@@ -1435,7 +1456,7 @@ pub fn build_session(
match profiler {
Ok(profiler) => Some(Arc::new(profiler)),
Err(e) => {
- early_warn(sopts.error_format, format!("failed to create profiler: {e}"));
+ handler.early_warn(format!("failed to create profiler: {e}"));
None
}
}
@@ -1675,6 +1696,13 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
if sess.opts.unstable_opts.instrument_xray.is_some() && !sess.target.options.supports_xray {
sess.emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() });
}
+
+ if let Some(flavor) = sess.opts.cg.linker_flavor {
+ if let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) {
+ let flavor = flavor.desc();
+ sess.emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list });
+ }
+ }
}
/// Holds data on the current incremental compilation session, if there is one.
@@ -1695,7 +1723,64 @@ pub enum IncrCompSession {
InvalidBecauseOfErrors { session_directory: PathBuf },
}
-fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler {
+/// A wrapper around an [`Handler`] that is used for early error emissions.
+pub struct EarlyErrorHandler {
+ handler: Handler,
+}
+
+impl EarlyErrorHandler {
+ pub fn new(output: ErrorOutputType) -> Self {
+ let emitter = mk_emitter(output);
+ Self { handler: rustc_errors::Handler::with_emitter(true, None, emitter) }
+ }
+
+ pub fn abort_if_errors(&self) {
+ self.handler.abort_if_errors()
+ }
+
+ /// Swap out the underlying handler once we acquire the user's preference on error emission
+ /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
+ /// previous handler will be emitted.
+ pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) {
+ self.handler.abort_if_errors();
+
+ let emitter = mk_emitter(output);
+ self.handler = Handler::with_emitter(true, None, emitter);
+ }
+
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ pub fn early_note(&self, msg: impl Into<DiagnosticMessage>) {
+ self.handler.struct_note_without_error(msg).emit()
+ }
+
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ pub fn early_help(&self, msg: impl Into<DiagnosticMessage>) {
+ self.handler.struct_help(msg).emit()
+ }
+
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
+ pub fn early_error_no_abort(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
+ self.handler.struct_err(msg).emit()
+ }
+
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ pub fn early_error(&self, msg: impl Into<DiagnosticMessage>) -> ! {
+ self.handler.struct_fatal(msg).emit()
+ }
+
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
+ self.handler.struct_warn(msg).emit()
+ }
+}
+
+fn mk_emitter(output: ErrorOutputType) -> Box<dyn Emitter + sync::Send + 'static> {
// FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will
// need to reference every crate that might emit an early error for translation to work.
let fallback_bundle =
@@ -1727,27 +1812,5 @@ fn early_error_handler(output: config::ErrorOutputType) -> rustc_errors::Handler
TerminalUrl::No,
)),
};
- rustc_errors::Handler::with_emitter(true, None, emitter)
-}
-
-#[allow(rustc::untranslatable_diagnostic)]
-#[allow(rustc::diagnostic_outside_of_impl)]
-#[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
-pub fn early_error_no_abort(
- output: config::ErrorOutputType,
- msg: impl Into<DiagnosticMessage>,
-) -> ErrorGuaranteed {
- early_error_handler(output).struct_err(msg).emit()
-}
-
-#[allow(rustc::untranslatable_diagnostic)]
-#[allow(rustc::diagnostic_outside_of_impl)]
-pub fn early_error(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) -> ! {
- early_error_handler(output).struct_fatal(msg).emit()
-}
-
-#[allow(rustc::untranslatable_diagnostic)]
-#[allow(rustc::diagnostic_outside_of_impl)]
-pub fn early_warn(output: config::ErrorOutputType, msg: impl Into<DiagnosticMessage>) {
- early_error_handler(output).struct_warn(msg).emit()
+ emitter
}
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 80360a3c7..a6e6de5f7 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -8,6 +8,7 @@ rustc_hir = { path = "../rustc_hir" }
rustc_middle = { path = "../rustc_middle", optional = true }
rustc_span = { path = "../rustc_span", optional = true }
tracing = "0.1"
+scoped-tls = "1.0"
[features]
default = [
diff --git a/compiler/rustc_smir/src/lib.rs b/compiler/rustc_smir/src/lib.rs
index b00f0a1c1..fb03633b9 100644
--- a/compiler/rustc_smir/src/lib.rs
+++ b/compiler/rustc_smir/src/lib.rs
@@ -19,3 +19,6 @@ pub mod stable_mir;
// Make this module private for now since external users should not call these directly.
mod rustc_smir;
+
+#[macro_use]
+extern crate scoped_tls;
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index 5572108f4..85d5bb00c 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -7,7 +7,9 @@
//!
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
-use crate::stable_mir::{self, ty::TyKind, Context};
+use crate::stable_mir::ty::{FloatTy, IntTy, RigidTy, TyKind, UintTy};
+use crate::stable_mir::{self, Context};
+use rustc_middle::mir;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
use tracing::debug;
@@ -42,8 +44,8 @@ impl<'tcx> Context for Tables<'tcx> {
.basic_blocks
.iter()
.map(|block| stable_mir::mir::BasicBlock {
- terminator: rustc_terminator_to_terminator(block.terminator()),
- statements: block.statements.iter().map(rustc_statement_to_statement).collect(),
+ terminator: block.terminator().stable(),
+ statements: block.statements.iter().map(mir::Statement::stable).collect(),
})
.collect(),
locals: mir.local_decls.iter().map(|decl| self.intern_ty(decl.ty)).collect(),
@@ -68,11 +70,28 @@ pub struct Tables<'tcx> {
impl<'tcx> Tables<'tcx> {
fn rustc_ty_to_ty(&mut self, ty: Ty<'tcx>) -> TyKind {
match ty.kind() {
- ty::Bool => TyKind::Bool,
- ty::Char => todo!(),
- ty::Int(_) => todo!(),
- ty::Uint(_) => todo!(),
- ty::Float(_) => todo!(),
+ ty::Bool => TyKind::RigidTy(RigidTy::Bool),
+ ty::Char => TyKind::RigidTy(RigidTy::Char),
+ ty::Int(int_ty) => match int_ty {
+ ty::IntTy::Isize => TyKind::RigidTy(RigidTy::Int(IntTy::Isize)),
+ ty::IntTy::I8 => TyKind::RigidTy(RigidTy::Int(IntTy::I8)),
+ ty::IntTy::I16 => TyKind::RigidTy(RigidTy::Int(IntTy::I16)),
+ ty::IntTy::I32 => TyKind::RigidTy(RigidTy::Int(IntTy::I32)),
+ ty::IntTy::I64 => TyKind::RigidTy(RigidTy::Int(IntTy::I64)),
+ ty::IntTy::I128 => TyKind::RigidTy(RigidTy::Int(IntTy::I128)),
+ },
+ ty::Uint(uint_ty) => match uint_ty {
+ ty::UintTy::Usize => TyKind::RigidTy(RigidTy::Uint(UintTy::Usize)),
+ ty::UintTy::U8 => TyKind::RigidTy(RigidTy::Uint(UintTy::U8)),
+ ty::UintTy::U16 => TyKind::RigidTy(RigidTy::Uint(UintTy::U16)),
+ ty::UintTy::U32 => TyKind::RigidTy(RigidTy::Uint(UintTy::U32)),
+ ty::UintTy::U64 => TyKind::RigidTy(RigidTy::Uint(UintTy::U64)),
+ ty::UintTy::U128 => TyKind::RigidTy(RigidTy::Uint(UintTy::U128)),
+ },
+ ty::Float(float_ty) => match float_ty {
+ ty::FloatTy::F32 => TyKind::RigidTy(RigidTy::Float(FloatTy::F32)),
+ ty::FloatTy::F64 => TyKind::RigidTy(RigidTy::Float(FloatTy::F64)),
+ },
ty::Adt(_, _) => todo!(),
ty::Foreign(_) => todo!(),
ty::Str => todo!(),
@@ -89,9 +108,9 @@ impl<'tcx> Tables<'tcx> {
ty::GeneratorWitness(_) => todo!(),
ty::GeneratorWitnessMIR(_, _) => todo!(),
ty::Never => todo!(),
- ty::Tuple(fields) => {
- TyKind::Tuple(fields.iter().map(|ty| self.intern_ty(ty)).collect())
- }
+ ty::Tuple(fields) => TyKind::RigidTy(RigidTy::Tuple(
+ fields.iter().map(|ty| self.intern_ty(ty)).collect(),
+ )),
ty::Alias(_, _) => todo!(),
ty::Param(_) => todo!(),
ty::Bound(_, _) => todo!(),
@@ -118,82 +137,95 @@ fn smir_crate(tcx: TyCtxt<'_>, crate_num: CrateNum) -> stable_mir::Crate {
stable_mir::Crate { id: crate_num.into(), name: crate_name, is_local }
}
-fn rustc_statement_to_statement(
- s: &rustc_middle::mir::Statement<'_>,
-) -> stable_mir::mir::Statement {
- use rustc_middle::mir::StatementKind::*;
- match &s.kind {
- Assign(assign) => stable_mir::mir::Statement::Assign(
- rustc_place_to_place(&assign.0),
- rustc_rvalue_to_rvalue(&assign.1),
- ),
- FakeRead(_) => todo!(),
- SetDiscriminant { .. } => todo!(),
- Deinit(_) => todo!(),
- StorageLive(_) => todo!(),
- StorageDead(_) => todo!(),
- Retag(_, _) => todo!(),
- PlaceMention(_) => todo!(),
- AscribeUserType(_, _) => todo!(),
- Coverage(_) => todo!(),
- Intrinsic(_) => todo!(),
- ConstEvalCounter => todo!(),
- Nop => stable_mir::mir::Statement::Nop,
+pub trait Stable {
+ type T;
+ fn stable(&self) -> Self::T;
+}
+
+impl<'tcx> Stable for mir::Statement<'tcx> {
+ type T = stable_mir::mir::Statement;
+ fn stable(&self) -> Self::T {
+ use rustc_middle::mir::StatementKind::*;
+ match &self.kind {
+ Assign(assign) => {
+ stable_mir::mir::Statement::Assign(assign.0.stable(), assign.1.stable())
+ }
+ FakeRead(_) => todo!(),
+ SetDiscriminant { .. } => todo!(),
+ Deinit(_) => todo!(),
+ StorageLive(_) => todo!(),
+ StorageDead(_) => todo!(),
+ Retag(_, _) => todo!(),
+ PlaceMention(_) => todo!(),
+ AscribeUserType(_, _) => todo!(),
+ Coverage(_) => todo!(),
+ Intrinsic(_) => todo!(),
+ ConstEvalCounter => todo!(),
+ Nop => stable_mir::mir::Statement::Nop,
+ }
}
}
-fn rustc_rvalue_to_rvalue(rvalue: &rustc_middle::mir::Rvalue<'_>) -> stable_mir::mir::Rvalue {
- use rustc_middle::mir::Rvalue::*;
- match rvalue {
- Use(op) => stable_mir::mir::Rvalue::Use(rustc_op_to_op(op)),
- Repeat(_, _) => todo!(),
- Ref(_, _, _) => todo!(),
- ThreadLocalRef(_) => todo!(),
- AddressOf(_, _) => todo!(),
- Len(_) => todo!(),
- Cast(_, _, _) => todo!(),
- BinaryOp(_, _) => todo!(),
- CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp(
- rustc_bin_op_to_bin_op(bin_op),
- rustc_op_to_op(&ops.0),
- rustc_op_to_op(&ops.1),
- ),
- NullaryOp(_, _) => todo!(),
- UnaryOp(un_op, op) => {
- stable_mir::mir::Rvalue::UnaryOp(rustc_un_op_to_un_op(un_op), rustc_op_to_op(op))
+impl<'tcx> Stable for mir::Rvalue<'tcx> {
+ type T = stable_mir::mir::Rvalue;
+ fn stable(&self) -> Self::T {
+ use mir::Rvalue::*;
+ match self {
+ Use(op) => stable_mir::mir::Rvalue::Use(op.stable()),
+ Repeat(_, _) => todo!(),
+ Ref(_, _, _) => todo!(),
+ ThreadLocalRef(_) => todo!(),
+ AddressOf(_, _) => todo!(),
+ Len(_) => todo!(),
+ Cast(_, _, _) => todo!(),
+ BinaryOp(_, _) => todo!(),
+ CheckedBinaryOp(bin_op, ops) => stable_mir::mir::Rvalue::CheckedBinaryOp(
+ bin_op.stable(),
+ ops.0.stable(),
+ ops.1.stable(),
+ ),
+ NullaryOp(_, _) => todo!(),
+ UnaryOp(un_op, op) => stable_mir::mir::Rvalue::UnaryOp(un_op.stable(), op.stable()),
+ Discriminant(_) => todo!(),
+ Aggregate(_, _) => todo!(),
+ ShallowInitBox(_, _) => todo!(),
+ CopyForDeref(_) => todo!(),
}
- Discriminant(_) => todo!(),
- Aggregate(_, _) => todo!(),
- ShallowInitBox(_, _) => todo!(),
- CopyForDeref(_) => todo!(),
}
}
-fn rustc_op_to_op(op: &rustc_middle::mir::Operand<'_>) -> stable_mir::mir::Operand {
- use rustc_middle::mir::Operand::*;
- match op {
- Copy(place) => stable_mir::mir::Operand::Copy(rustc_place_to_place(place)),
- Move(place) => stable_mir::mir::Operand::Move(rustc_place_to_place(place)),
- Constant(c) => stable_mir::mir::Operand::Constant(c.to_string()),
+impl<'tcx> Stable for mir::Operand<'tcx> {
+ type T = stable_mir::mir::Operand;
+ fn stable(&self) -> Self::T {
+ use mir::Operand::*;
+ match self {
+ Copy(place) => stable_mir::mir::Operand::Copy(place.stable()),
+ Move(place) => stable_mir::mir::Operand::Move(place.stable()),
+ Constant(c) => stable_mir::mir::Operand::Constant(c.to_string()),
+ }
}
}
-fn rustc_place_to_place(place: &rustc_middle::mir::Place<'_>) -> stable_mir::mir::Place {
- stable_mir::mir::Place {
- local: place.local.as_usize(),
- projection: format!("{:?}", place.projection),
+impl<'tcx> Stable for mir::Place<'tcx> {
+ type T = stable_mir::mir::Place;
+ fn stable(&self) -> Self::T {
+ stable_mir::mir::Place {
+ local: self.local.as_usize(),
+ projection: format!("{:?}", self.projection),
+ }
}
}
-fn rustc_unwind_to_unwind(
- unwind: &rustc_middle::mir::UnwindAction,
-) -> stable_mir::mir::UnwindAction {
- use rustc_middle::mir::UnwindAction;
- match unwind {
- UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue,
- UnwindAction::Unreachable => stable_mir::mir::UnwindAction::Unreachable,
- UnwindAction::Terminate => stable_mir::mir::UnwindAction::Terminate,
- UnwindAction::Cleanup(bb) => stable_mir::mir::UnwindAction::Cleanup(bb.as_usize()),
+impl Stable for mir::UnwindAction {
+ type T = stable_mir::mir::UnwindAction;
+ fn stable(&self) -> Self::T {
+ use rustc_middle::mir::UnwindAction;
+ match self {
+ UnwindAction::Continue => stable_mir::mir::UnwindAction::Continue,
+ UnwindAction::Unreachable => stable_mir::mir::UnwindAction::Unreachable,
+ UnwindAction::Terminate => stable_mir::mir::UnwindAction::Terminate,
+ UnwindAction::Cleanup(bb) => stable_mir::mir::UnwindAction::Cleanup(bb.as_usize()),
+ }
}
}
@@ -202,138 +234,168 @@ fn rustc_assert_msg_to_msg<'tcx>(
) -> stable_mir::mir::AssertMessage {
use rustc_middle::mir::AssertKind;
match assert_message {
- AssertKind::BoundsCheck { len, index } => stable_mir::mir::AssertMessage::BoundsCheck {
- len: rustc_op_to_op(len),
- index: rustc_op_to_op(index),
- },
- AssertKind::Overflow(bin_op, op1, op2) => stable_mir::mir::AssertMessage::Overflow(
- rustc_bin_op_to_bin_op(bin_op),
- rustc_op_to_op(op1),
- rustc_op_to_op(op2),
- ),
- AssertKind::OverflowNeg(op) => {
- stable_mir::mir::AssertMessage::OverflowNeg(rustc_op_to_op(op))
+ AssertKind::BoundsCheck { len, index } => {
+ stable_mir::mir::AssertMessage::BoundsCheck { len: len.stable(), index: index.stable() }
+ }
+ AssertKind::Overflow(bin_op, op1, op2) => {
+ stable_mir::mir::AssertMessage::Overflow(bin_op.stable(), op1.stable(), op2.stable())
}
+ AssertKind::OverflowNeg(op) => stable_mir::mir::AssertMessage::OverflowNeg(op.stable()),
AssertKind::DivisionByZero(op) => {
- stable_mir::mir::AssertMessage::DivisionByZero(rustc_op_to_op(op))
+ stable_mir::mir::AssertMessage::DivisionByZero(op.stable())
}
AssertKind::RemainderByZero(op) => {
- stable_mir::mir::AssertMessage::RemainderByZero(rustc_op_to_op(op))
+ stable_mir::mir::AssertMessage::RemainderByZero(op.stable())
}
AssertKind::ResumedAfterReturn(generator) => {
- stable_mir::mir::AssertMessage::ResumedAfterReturn(rustc_generator_to_generator(
- generator,
- ))
+ stable_mir::mir::AssertMessage::ResumedAfterReturn(generator.stable())
}
AssertKind::ResumedAfterPanic(generator) => {
- stable_mir::mir::AssertMessage::ResumedAfterPanic(rustc_generator_to_generator(
- generator,
- ))
+ stable_mir::mir::AssertMessage::ResumedAfterPanic(generator.stable())
}
AssertKind::MisalignedPointerDereference { required, found } => {
stable_mir::mir::AssertMessage::MisalignedPointerDereference {
- required: rustc_op_to_op(required),
- found: rustc_op_to_op(found),
+ required: required.stable(),
+ found: found.stable(),
}
}
}
}
-fn rustc_bin_op_to_bin_op(bin_op: &rustc_middle::mir::BinOp) -> stable_mir::mir::BinOp {
- use rustc_middle::mir::BinOp;
- match bin_op {
- BinOp::Add => stable_mir::mir::BinOp::Add,
- BinOp::Sub => stable_mir::mir::BinOp::Sub,
- BinOp::Mul => stable_mir::mir::BinOp::Mul,
- BinOp::Div => stable_mir::mir::BinOp::Div,
- BinOp::Rem => stable_mir::mir::BinOp::Rem,
- BinOp::BitXor => stable_mir::mir::BinOp::BitXor,
- BinOp::BitAnd => stable_mir::mir::BinOp::BitAnd,
- BinOp::BitOr => stable_mir::mir::BinOp::BitOr,
- BinOp::Shl => stable_mir::mir::BinOp::Shl,
- BinOp::Shr => stable_mir::mir::BinOp::Shr,
- BinOp::Eq => stable_mir::mir::BinOp::Eq,
- BinOp::Lt => stable_mir::mir::BinOp::Lt,
- BinOp::Le => stable_mir::mir::BinOp::Le,
- BinOp::Ne => stable_mir::mir::BinOp::Ne,
- BinOp::Ge => stable_mir::mir::BinOp::Ge,
- BinOp::Gt => stable_mir::mir::BinOp::Gt,
- BinOp::Offset => stable_mir::mir::BinOp::Offset,
+impl Stable for mir::BinOp {
+ type T = stable_mir::mir::BinOp;
+ fn stable(&self) -> Self::T {
+ use mir::BinOp;
+ match self {
+ BinOp::Add => stable_mir::mir::BinOp::Add,
+ BinOp::AddUnchecked => stable_mir::mir::BinOp::AddUnchecked,
+ BinOp::Sub => stable_mir::mir::BinOp::Sub,
+ BinOp::SubUnchecked => stable_mir::mir::BinOp::SubUnchecked,
+ BinOp::Mul => stable_mir::mir::BinOp::Mul,
+ BinOp::MulUnchecked => stable_mir::mir::BinOp::MulUnchecked,
+ BinOp::Div => stable_mir::mir::BinOp::Div,
+ BinOp::Rem => stable_mir::mir::BinOp::Rem,
+ BinOp::BitXor => stable_mir::mir::BinOp::BitXor,
+ BinOp::BitAnd => stable_mir::mir::BinOp::BitAnd,
+ BinOp::BitOr => stable_mir::mir::BinOp::BitOr,
+ BinOp::Shl => stable_mir::mir::BinOp::Shl,
+ BinOp::ShlUnchecked => stable_mir::mir::BinOp::ShlUnchecked,
+ BinOp::Shr => stable_mir::mir::BinOp::Shr,
+ BinOp::ShrUnchecked => stable_mir::mir::BinOp::ShrUnchecked,
+ BinOp::Eq => stable_mir::mir::BinOp::Eq,
+ BinOp::Lt => stable_mir::mir::BinOp::Lt,
+ BinOp::Le => stable_mir::mir::BinOp::Le,
+ BinOp::Ne => stable_mir::mir::BinOp::Ne,
+ BinOp::Ge => stable_mir::mir::BinOp::Ge,
+ BinOp::Gt => stable_mir::mir::BinOp::Gt,
+ BinOp::Offset => stable_mir::mir::BinOp::Offset,
+ }
}
}
-fn rustc_un_op_to_un_op(unary_op: &rustc_middle::mir::UnOp) -> stable_mir::mir::UnOp {
- use rustc_middle::mir::UnOp;
- match unary_op {
- UnOp::Not => stable_mir::mir::UnOp::Not,
- UnOp::Neg => stable_mir::mir::UnOp::Neg,
+impl Stable for mir::UnOp {
+ type T = stable_mir::mir::UnOp;
+ fn stable(&self) -> Self::T {
+ use mir::UnOp;
+ match self {
+ UnOp::Not => stable_mir::mir::UnOp::Not,
+ UnOp::Neg => stable_mir::mir::UnOp::Neg,
+ }
}
}
-fn rustc_generator_to_generator(
- generator: &rustc_hir::GeneratorKind,
-) -> stable_mir::mir::GeneratorKind {
- use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
- match generator {
- GeneratorKind::Async(async_gen) => {
- let async_gen = match async_gen {
- AsyncGeneratorKind::Block => stable_mir::mir::AsyncGeneratorKind::Block,
- AsyncGeneratorKind::Closure => stable_mir::mir::AsyncGeneratorKind::Closure,
- AsyncGeneratorKind::Fn => stable_mir::mir::AsyncGeneratorKind::Fn,
- };
- stable_mir::mir::GeneratorKind::Async(async_gen)
+impl Stable for rustc_hir::GeneratorKind {
+ type T = stable_mir::mir::GeneratorKind;
+ fn stable(&self) -> Self::T {
+ use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
+ match self {
+ GeneratorKind::Async(async_gen) => {
+ let async_gen = match async_gen {
+ AsyncGeneratorKind::Block => stable_mir::mir::AsyncGeneratorKind::Block,
+ AsyncGeneratorKind::Closure => stable_mir::mir::AsyncGeneratorKind::Closure,
+ AsyncGeneratorKind::Fn => stable_mir::mir::AsyncGeneratorKind::Fn,
+ };
+ stable_mir::mir::GeneratorKind::Async(async_gen)
+ }
+ GeneratorKind::Gen => stable_mir::mir::GeneratorKind::Gen,
}
- GeneratorKind::Gen => stable_mir::mir::GeneratorKind::Gen,
}
}
-fn rustc_terminator_to_terminator(
- terminator: &rustc_middle::mir::Terminator<'_>,
-) -> stable_mir::mir::Terminator {
- use rustc_middle::mir::TerminatorKind::*;
- use stable_mir::mir::Terminator;
- match &terminator.kind {
- Goto { target } => Terminator::Goto { target: target.as_usize() },
- SwitchInt { discr, targets } => Terminator::SwitchInt {
- discr: rustc_op_to_op(discr),
- targets: targets
- .iter()
- .map(|(value, target)| stable_mir::mir::SwitchTarget {
- value,
- target: target.as_usize(),
- })
- .collect(),
- otherwise: targets.otherwise().as_usize(),
- },
- Resume => Terminator::Resume,
- Terminate => Terminator::Abort,
- Return => Terminator::Return,
- Unreachable => Terminator::Unreachable,
- Drop { place, target, unwind, replace: _ } => Terminator::Drop {
- place: rustc_place_to_place(place),
- target: target.as_usize(),
- unwind: rustc_unwind_to_unwind(unwind),
- },
- Call { func, args, destination, target, unwind, from_hir_call: _, fn_span: _ } => {
- Terminator::Call {
- func: rustc_op_to_op(func),
- args: args.iter().map(|arg| rustc_op_to_op(arg)).collect(),
- destination: rustc_place_to_place(destination),
- target: target.map(|t| t.as_usize()),
- unwind: rustc_unwind_to_unwind(unwind),
+impl<'tcx> Stable for mir::InlineAsmOperand<'tcx> {
+ type T = stable_mir::mir::InlineAsmOperand;
+ fn stable(&self) -> Self::T {
+ use rustc_middle::mir::InlineAsmOperand;
+
+ let (in_value, out_place) = match self {
+ InlineAsmOperand::In { value, .. } => (Some(value.stable()), None),
+ InlineAsmOperand::Out { place, .. } => (None, place.map(|place| place.stable())),
+ InlineAsmOperand::InOut { in_value, out_place, .. } => {
+ (Some(in_value.stable()), out_place.map(|place| place.stable()))
+ }
+ InlineAsmOperand::Const { .. }
+ | InlineAsmOperand::SymFn { .. }
+ | InlineAsmOperand::SymStatic { .. } => (None, None),
+ };
+
+ stable_mir::mir::InlineAsmOperand { in_value, out_place, raw_rpr: format!("{:?}", self) }
+ }
+}
+
+impl<'tcx> Stable for mir::Terminator<'tcx> {
+ type T = stable_mir::mir::Terminator;
+ fn stable(&self) -> Self::T {
+ use rustc_middle::mir::TerminatorKind::*;
+ use stable_mir::mir::Terminator;
+ match &self.kind {
+ Goto { target } => Terminator::Goto { target: target.as_usize() },
+ SwitchInt { discr, targets } => Terminator::SwitchInt {
+ discr: discr.stable(),
+ targets: targets
+ .iter()
+ .map(|(value, target)| stable_mir::mir::SwitchTarget {
+ value,
+ target: target.as_usize(),
+ })
+ .collect(),
+ otherwise: targets.otherwise().as_usize(),
+ },
+ Resume => Terminator::Resume,
+ Terminate => Terminator::Abort,
+ Return => Terminator::Return,
+ Unreachable => Terminator::Unreachable,
+ Drop { place, target, unwind, replace: _ } => Terminator::Drop {
+ place: place.stable(),
+ target: target.as_usize(),
+ unwind: unwind.stable(),
+ },
+ Call { func, args, destination, target, unwind, call_source: _, fn_span: _ } => {
+ Terminator::Call {
+ func: func.stable(),
+ args: args.iter().map(|arg| arg.stable()).collect(),
+ destination: destination.stable(),
+ target: target.map(|t| t.as_usize()),
+ unwind: unwind.stable(),
+ }
+ }
+ Assert { cond, expected, msg, target, unwind } => Terminator::Assert {
+ cond: cond.stable(),
+ expected: *expected,
+ msg: rustc_assert_msg_to_msg(msg),
+ target: target.as_usize(),
+ unwind: unwind.stable(),
+ },
+ InlineAsm { template, operands, options, line_spans, destination, unwind } => {
+ Terminator::InlineAsm {
+ template: format!("{:?}", template),
+ operands: operands.iter().map(|operand| operand.stable()).collect(),
+ options: format!("{:?}", options),
+ line_spans: format!("{:?}", line_spans),
+ destination: destination.map(|d| d.as_usize()),
+ unwind: unwind.stable(),
+ }
}
+ Yield { .. } | GeneratorDrop | FalseEdge { .. } | FalseUnwind { .. } => unreachable!(),
}
- Assert { cond, expected, msg, target, unwind } => Terminator::Assert {
- cond: rustc_op_to_op(cond),
- expected: *expected,
- msg: rustc_assert_msg_to_msg(msg),
- target: target.as_usize(),
- unwind: rustc_unwind_to_unwind(unwind),
- },
- Yield { .. } => todo!(),
- GeneratorDrop => Terminator::GeneratorDrop,
- FalseEdge { .. } => todo!(),
- FalseUnwind { .. } => todo!(),
- InlineAsm { .. } => todo!(),
}
}
diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs
index 6328c35aa..468e915d1 100644
--- a/compiler/rustc_smir/src/stable_mir/mir/body.rs
+++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs
@@ -46,6 +46,23 @@ pub enum Terminator {
unwind: UnwindAction,
},
GeneratorDrop,
+ InlineAsm {
+ template: String,
+ operands: Vec<InlineAsmOperand>,
+ options: String,
+ line_spans: String,
+ destination: Option<usize>,
+ unwind: UnwindAction,
+ },
+}
+
+#[derive(Clone, Debug)]
+pub struct InlineAsmOperand {
+ pub in_value: Option<Operand>,
+ pub out_place: Option<Place>,
+ // This field has a raw debug representation of MIR's InlineAsmOperand.
+ // For now we care about place/operand + the rest in a debug format.
+ pub raw_rpr: String,
}
#[derive(Clone, Debug)]
@@ -71,15 +88,20 @@ pub enum AssertMessage {
#[derive(Clone, Debug)]
pub enum BinOp {
Add,
+ AddUnchecked,
Sub,
+ SubUnchecked,
Mul,
+ MulUnchecked,
Div,
Rem,
BitXor,
BitAnd,
BitOr,
Shl,
+ ShlUnchecked,
Shr,
+ ShrUnchecked,
Eq,
Lt,
Le,
diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs
index 612777b9c..5e599a77b 100644
--- a/compiler/rustc_smir/src/stable_mir/mod.rs
+++ b/compiler/rustc_smir/src/stable_mir/mod.rs
@@ -100,18 +100,17 @@ pub trait Context {
fn rustc_tables(&mut self, f: &mut dyn FnMut(&mut Tables<'_>));
}
-thread_local! {
- /// A thread local variable that stores a pointer to the tables mapping between TyCtxt
- /// datastructures and stable MIR datastructures.
- static TLV: Cell<*mut ()> = const { Cell::new(std::ptr::null_mut()) };
-}
+// A thread local variable that stores a pointer to the tables mapping between TyCtxt
+// datastructures and stable MIR datastructures
+scoped_thread_local! (static TLV: Cell<*mut ()>);
pub fn run(mut context: impl Context, f: impl FnOnce()) {
- assert!(TLV.get().is_null());
+ assert!(!TLV.is_set());
fn g<'a>(mut context: &mut (dyn Context + 'a), f: impl FnOnce()) {
- TLV.set(&mut context as *mut &mut _ as _);
- f();
- TLV.replace(std::ptr::null_mut());
+ let ptr: *mut () = &mut context as *mut &mut _ as _;
+ TLV.set(&Cell::new(ptr), || {
+ f();
+ });
}
g(&mut context, f);
}
@@ -119,9 +118,10 @@ pub fn run(mut context: impl Context, f: impl FnOnce()) {
/// Loads the current context and calls a function with it.
/// Do not nest these, as that will ICE.
pub(crate) fn with<R>(f: impl FnOnce(&mut dyn Context) -> R) -> R {
- let ptr = TLV.replace(std::ptr::null_mut()) as *mut &mut dyn Context;
- assert!(!ptr.is_null());
- let ret = f(unsafe { *ptr });
- TLV.set(ptr as _);
- ret
+ assert!(TLV.is_set());
+ TLV.with(|tlv| {
+ let ptr = tlv.get();
+ assert!(!ptr.is_null());
+ f(unsafe { *(ptr as *mut &mut dyn Context) })
+ })
}
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index f27801b0f..3181af46e 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -9,7 +9,43 @@ impl Ty {
}
}
+#[derive(Clone, Debug)]
pub enum TyKind {
+ RigidTy(RigidTy),
+}
+
+#[derive(Clone, Debug)]
+pub enum RigidTy {
Bool,
+ Char,
+ Int(IntTy),
+ Uint(UintTy),
+ Float(FloatTy),
Tuple(Vec<Ty>),
}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum IntTy {
+ Isize,
+ I8,
+ I16,
+ I32,
+ I64,
+ I128,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum UintTy {
+ Usize,
+ U8,
+ U16,
+ U32,
+ U64,
+ U128,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FloatTy {
+ F32,
+ F64,
+}
diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml
index a7c7575f3..ee93f74e7 100644
--- a/compiler/rustc_span/Cargo.toml
+++ b/compiler/rustc_span/Cargo.toml
@@ -18,4 +18,4 @@ tracing = "0.1"
sha1 = "0.10.0"
sha2 = "0.10.1"
md5 = { package = "md-5", version = "0.10.0" }
-indexmap = { version = "1.9.3" }
+indexmap = { version = "2.0.0" }
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index b219fde4d..9f2ff4378 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -507,7 +507,7 @@ impl HygieneData {
self.normalize_to_macro_rules(call_site_ctxt)
};
- if call_site_ctxt == SyntaxContext::root() {
+ if call_site_ctxt.is_root() {
return self.apply_mark_internal(ctxt, expn_id, transparency);
}
@@ -671,12 +671,17 @@ impl SyntaxContext {
}
#[inline]
- pub(crate) fn as_u32(self) -> u32 {
+ pub const fn is_root(self) -> bool {
+ self.0 == SyntaxContext::root().as_u32()
+ }
+
+ #[inline]
+ pub(crate) const fn as_u32(self) -> u32 {
self.0
}
#[inline]
- pub(crate) fn from_u32(raw: u32) -> SyntaxContext {
+ pub(crate) const fn from_u32(raw: u32) -> SyntaxContext {
SyntaxContext(raw)
}
@@ -1288,7 +1293,7 @@ pub fn decode_expn_id(
decode_data: impl FnOnce(ExpnId) -> (ExpnData, ExpnHash),
) -> ExpnId {
if index == 0 {
- debug!("decode_expn_id: deserialized root");
+ trace!("decode_expn_id: deserialized root");
return ExpnId::root();
}
@@ -1321,7 +1326,7 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
) -> SyntaxContext {
let raw_id: u32 = Decodable::decode(d);
if raw_id == 0 {
- debug!("decode_syntax_context: deserialized root");
+ trace!("decode_syntax_context: deserialized root");
// The root is special
return SyntaxContext::root();
}
@@ -1500,7 +1505,7 @@ impl<CTX: HashStableContext> HashStable<CTX> for SyntaxContext {
const TAG_EXPANSION: u8 = 0;
const TAG_NO_EXPANSION: u8 = 1;
- if *self == SyntaxContext::root() {
+ if self.is_root() {
TAG_NO_EXPANSION.hash_stable(ctx, hasher);
} else {
TAG_EXPANSION.hash_stable(ctx, hasher);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index eae3f0fa0..3bb9c4920 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -65,11 +65,11 @@ use rustc_data_structures::sync::{Lock, Lrc};
use std::borrow::Cow;
use std::cmp::{self, Ordering};
-use std::fmt;
use std::hash::Hash;
use std::ops::{Add, Range, Sub};
use std::path::{Path, PathBuf};
use std::str::FromStr;
+use std::{fmt, iter};
use md5::Digest;
use md5::Md5;
@@ -733,12 +733,15 @@ impl Span {
/// else returns the `ExpnData` for the macro definition
/// corresponding to the source callsite.
pub fn source_callee(self) -> Option<ExpnData> {
- fn source_callee(expn_data: ExpnData) -> ExpnData {
- let next_expn_data = expn_data.call_site.ctxt().outer_expn_data();
- if !next_expn_data.is_root() { source_callee(next_expn_data) } else { expn_data }
- }
let expn_data = self.ctxt().outer_expn_data();
- if !expn_data.is_root() { Some(source_callee(expn_data)) } else { None }
+
+ // Create an iterator of call site expansions
+ iter::successors(Some(expn_data), |expn_data| {
+ Some(expn_data.call_site.ctxt().outer_expn_data())
+ })
+ // Find the last expansion which is not root
+ .take_while(|expn_data| !expn_data.is_root())
+ .last()
}
/// Checks if a span is "internal" to a macro in which `#[unstable]`
@@ -777,7 +780,7 @@ impl Span {
pub fn macro_backtrace(mut self) -> impl Iterator<Item = ExpnData> {
let mut prev_span = DUMMY_SP;
- std::iter::from_fn(move || {
+ iter::from_fn(move || {
loop {
let expn_data = self.ctxt().outer_expn_data();
if expn_data.is_root() {
@@ -826,9 +829,9 @@ impl Span {
// Return the macro span on its own to avoid weird diagnostic output. It is preferable to
// have an incomplete span than a completely nonsensical one.
if span_data.ctxt != end_data.ctxt {
- if span_data.ctxt == SyntaxContext::root() {
+ if span_data.ctxt.is_root() {
return end;
- } else if end_data.ctxt == SyntaxContext::root() {
+ } else if end_data.ctxt.is_root() {
return self;
}
// Both spans fall within a macro.
@@ -837,7 +840,7 @@ impl Span {
Span::new(
cmp::min(span_data.lo, end_data.lo),
cmp::max(span_data.hi, end_data.hi),
- if span_data.ctxt == SyntaxContext::root() { end_data.ctxt } else { span_data.ctxt },
+ if span_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
if span_data.parent == end_data.parent { span_data.parent } else { None },
)
}
@@ -855,7 +858,7 @@ impl Span {
Span::new(
span.hi,
end.lo,
- if end.ctxt == SyntaxContext::root() { end.ctxt } else { span.ctxt },
+ if end.ctxt.is_root() { end.ctxt } else { span.ctxt },
if span.parent == end.parent { span.parent } else { None },
)
}
@@ -879,9 +882,9 @@ impl Span {
// Return the macro span on its own to avoid weird diagnostic output. It is preferable to
// have an incomplete span than a completely nonsensical one.
if span_data.ctxt != end_data.ctxt {
- if span_data.ctxt == SyntaxContext::root() {
+ if span_data.ctxt.is_root() {
return end;
- } else if end_data.ctxt == SyntaxContext::root() {
+ } else if end_data.ctxt.is_root() {
return self;
}
// Both spans fall within a macro.
@@ -890,7 +893,7 @@ impl Span {
Span::new(
span_data.lo,
end_data.lo,
- if end_data.ctxt == SyntaxContext::root() { end_data.ctxt } else { span_data.ctxt },
+ if end_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
if span_data.parent == end_data.parent { span_data.parent } else { None },
)
}
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 1824510a9..86716da17 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -744,6 +744,21 @@ impl SourceMap {
})
}
+ /// Extends the given `Span` to previous character while the previous character matches the predicate
+ pub fn span_extend_prev_while(
+ &self,
+ span: Span,
+ f: impl Fn(char) -> bool,
+ ) -> Result<Span, SpanSnippetError> {
+ self.span_to_source(span, |s, start, _end| {
+ let n = s[..start]
+ .char_indices()
+ .rfind(|&(_, c)| !f(c))
+ .map_or(start, |(i, _)| start - i - 1);
+ Ok(span.with_lo(span.lo() - BytePos(n as u32)))
+ })
+ }
+
/// Extends the given `Span` to just before the next occurrence of `c`.
pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span {
if let Ok(next_source) = self.span_to_next_source(sp) {
@@ -1057,11 +1072,7 @@ impl SourceMap {
/// This index is guaranteed to be valid for the lifetime of this `SourceMap`,
/// since `source_files` is a `MonotonicVec`
pub fn lookup_source_file_idx(&self, pos: BytePos) -> usize {
- self.files
- .borrow()
- .source_files
- .binary_search_by_key(&pos, |key| key.start_pos)
- .unwrap_or_else(|p| p - 1)
+ self.files.borrow().source_files.partition_point(|x| x.start_pos <= pos) - 1
}
pub fn count_lines(&self) -> usize {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 874d578fe..5c6d43e50 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -432,7 +432,6 @@ symbols! {
bool,
borrowck_graphviz_format,
borrowck_graphviz_postflow,
- box_free,
box_new,
box_patterns,
box_syntax,
@@ -657,6 +656,7 @@ symbols! {
dyn_trait,
e,
edition_panic,
+ effects,
eh_catch_typeinfo,
eh_personality,
emit,
@@ -687,6 +687,7 @@ symbols! {
expf32,
expf64,
explicit_generic_args_with_impl_trait,
+ explicit_tail_calls,
export_name,
expr,
extended_key_value_attributes,
@@ -701,7 +702,9 @@ symbols! {
f,
f16c_target_feature,
f32,
+ f32_nan,
f64,
+ f64_nan,
fabsf32,
fabsf64,
fadd_fast,
@@ -756,7 +759,6 @@ symbols! {
from_desugaring,
from_fn,
from_iter,
- from_method,
from_output,
from_residual,
from_size_align_unchecked,
@@ -791,6 +793,7 @@ symbols! {
hexagon_target_feature,
hidden,
homogeneous_aggregate,
+ host,
html_favicon_url,
html_logo_url,
html_no_source,
@@ -815,6 +818,7 @@ symbols! {
impl_trait_in_bindings,
impl_trait_in_fn_trait_return,
impl_trait_projections,
+ implement_via_object,
implied_by,
import,
import_name_type,
@@ -868,6 +872,7 @@ symbols! {
large_assignments,
lateout,
lazy_normalization_consts,
+ lazy_type_alias,
le,
len,
let_chains,
@@ -1146,12 +1151,15 @@ symbols! {
profiler_builtins,
profiler_runtime,
ptr,
+ ptr_cast_mut,
+ ptr_from_ref,
ptr_guaranteed_cmp,
ptr_mask,
ptr_null,
ptr_null_mut,
ptr_offset_from,
ptr_offset_from_unsigned,
+ ptr_unique,
pub_macro_rules,
pub_restricted,
public,
@@ -1277,6 +1285,7 @@ symbols! {
rustc_evaluate_where_clauses,
rustc_expected_cgu_reuse,
rustc_has_incoherent_inherent_impls,
+ rustc_host,
rustc_if_this_changed,
rustc_inherit_overflow_checks,
rustc_insignificant_dtor,
@@ -1454,6 +1463,10 @@ symbols! {
stop_after_dataflow,
store,
str,
+ str_from_utf8,
+ str_from_utf8_mut,
+ str_from_utf8_unchecked,
+ str_from_utf8_unchecked_mut,
str_split_whitespace,
str_trim,
str_trim_end,
@@ -1547,6 +1560,7 @@ symbols! {
type_length_limit,
type_macros,
type_name,
+ type_privacy_lints,
u128,
u16,
u32,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 254ede4e6..ec7032cd3 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -230,7 +230,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
self.write_str("[")?;
self = self.print_type(ty)?;
self.write_str("; ")?;
- if let Some(size) = size.kind().try_to_bits(self.tcx().data_layout.pointer_size) {
+ if let Some(size) = size.try_to_bits(self.tcx().data_layout.pointer_size) {
write!(self, "{size}")?
} else if let ty::ConstKind::Param(param) = size.kind() {
self = param.print(self)?
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 9fa49123a..3b46275ec 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -10,7 +10,6 @@
use core::fmt::Display;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::DiagnosticMessage;
use rustc_hir as hir;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{
@@ -113,7 +112,7 @@ fn encode_const<'tcx>(
let _ = write!(s, "{value}");
}
- if let Some(scalar_int) = c.kind().try_to_scalar_int() {
+ if let Some(scalar_int) = c.try_to_scalar_int() {
let signed = c.ty().is_signed();
match scalar_int.size().bits() {
8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
@@ -502,7 +501,14 @@ fn encode_ty<'tcx>(
ty::Array(ty0, len) => {
// A<array-length><element-type>
let mut s = String::from("A");
- let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap());
+ let _ = write!(
+ s,
+ "{}",
+ &len.try_to_scalar()
+ .unwrap()
+ .to_u64()
+ .unwrap_or_else(|_| panic!("failed to convert length to u64"))
+ );
s.push_str(&encode_ty(tcx, *ty0, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
@@ -534,10 +540,7 @@ fn encode_ty<'tcx>(
tcx.sess
.struct_span_err(
cfi_encoding.span,
- DiagnosticMessage::Str(format!(
- "invalid `cfi_encoding` for `{:?}`",
- ty.kind()
- )),
+ format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
)
.emit();
}
@@ -589,10 +592,7 @@ fn encode_ty<'tcx>(
tcx.sess
.struct_span_err(
cfi_encoding.span,
- DiagnosticMessage::Str(format!(
- "invalid `cfi_encoding` for `{:?}`",
- ty.kind()
- )),
+ format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
)
.emit();
}
@@ -608,9 +608,7 @@ fn encode_ty<'tcx>(
}
// Function types
- ty::FnDef(def_id, substs)
- | ty::Closure(def_id, substs)
- | ty::Generator(def_id, substs, ..) => {
+ ty::FnDef(def_id, substs) | ty::Closure(def_id, substs) => {
// u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
// as vendor extended type.
let mut s = String::new();
@@ -621,6 +619,23 @@ fn encode_ty<'tcx>(
typeid.push_str(&s);
}
+ ty::Generator(def_id, substs, ..) => {
+ // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
+ // as vendor extended type.
+ let mut s = String::new();
+ let name = encode_ty_name(tcx, *def_id);
+ let _ = write!(s, "u{}{}", name.len(), &name);
+ // Encode parent substs only
+ s.push_str(&encode_substs(
+ tcx,
+ tcx.mk_substs(substs.as_generator().parent_substs()),
+ dict,
+ options,
+ ));
+ compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+ typeid.push_str(&s);
+ }
+
// Pointer types
ty::Ref(region, ty0, ..) => {
// [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
@@ -628,7 +643,7 @@ fn encode_ty<'tcx>(
s.push_str("u3refI");
s.push_str(&encode_ty(tcx, *ty0, dict, options));
s.push('E');
- compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
+ compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
if ty.is_mutable_ptr() {
s = format!("{}{}", "U3mut", &s);
compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
@@ -681,12 +696,12 @@ fn encode_ty<'tcx>(
}
// Unexpected types
- ty::Bound(..)
+ ty::Alias(..)
+ | ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
| ty::GeneratorWitnessMIR(..)
| ty::Infer(..)
- | ty::Alias(..)
| ty::Placeholder(..) => {
bug!("encode_ty: unexpected `{:?}`", ty.kind());
}
@@ -724,7 +739,7 @@ fn transform_substs<'tcx>(
options: TransformTyOptions,
) -> SubstsRef<'tcx> {
let substs = substs.iter().map(|subst| match subst.unpack() {
- GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(),
+ GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
_ => subst,
});
@@ -739,7 +754,12 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
let mut ty = ty;
match ty.kind() {
- ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) => {}
+ ty::Float(..)
+ | ty::Char
+ | ty::Str
+ | ty::Never
+ | ty::Foreign(..)
+ | ty::GeneratorWitness(..) => {}
ty::Bool => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
@@ -789,24 +809,28 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
_ if ty.is_unit() => {}
ty::Tuple(tys) => {
- ty = tcx.mk_tup_from_iter(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
+ ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options)));
}
ty::Array(ty0, len) => {
- let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap();
- ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
+ let len = len
+ .try_to_scalar()
+ .unwrap()
+ .to_u64()
+ .unwrap_or_else(|_| panic!("failed to convert length to u64"));
+ ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len);
}
ty::Slice(ty0) => {
- ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
+ ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options));
}
ty::Adt(adt_def, substs) => {
if ty.is_c_void(tcx) {
- ty = tcx.mk_unit();
+ ty = Ty::new_unit(tcx);
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
{
- ty = tcx.mk_adt(*adt_def, ty::List::empty());
+ ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
} else if adt_def.repr().transparent() && adt_def.is_struct() {
// Don't transform repr(transparent) types with an user-defined CFI encoding to
// preserve the user-defined CFI encoding.
@@ -837,37 +861,42 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
}
} else {
// Transform repr(transparent) types without non-ZST field into ()
- ty = tcx.mk_unit();
+ ty = Ty::new_unit(tcx);
}
} else {
- ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
+ ty = Ty::new_adt(tcx, *adt_def, transform_substs(tcx, substs, options));
}
}
ty::FnDef(def_id, substs) => {
- ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
+ ty = Ty::new_fn_def(tcx, *def_id, transform_substs(tcx, substs, options));
}
ty::Closure(def_id, substs) => {
- ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
+ ty = Ty::new_closure(tcx, *def_id, transform_substs(tcx, substs, options));
}
ty::Generator(def_id, substs, movability) => {
- ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
+ ty = Ty::new_generator(
+ tcx,
+ *def_id,
+ transform_substs(tcx, substs, options),
+ *movability,
+ );
}
ty::Ref(region, ty0, ..) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() {
- ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+ ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx));
} else {
- ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+ ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx));
}
} else {
if ty.is_mutable_ptr() {
- ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
+ ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options));
} else {
- ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
+ ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options));
}
}
}
@@ -875,22 +904,22 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
ty::RawPtr(tm) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() {
- ty = tcx.mk_mut_ptr(tcx.mk_unit());
+ ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx));
} else {
- ty = tcx.mk_imm_ptr(tcx.mk_unit());
+ ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx));
}
} else {
if ty.is_mutable_ptr() {
- ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
+ ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, tm.ty, options));
} else {
- ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
+ ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, tm.ty, options));
}
}
}
ty::FnPtr(fn_sig) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
- ty = tcx.mk_imm_ptr(tcx.mk_unit());
+ ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx));
} else {
let parameters: Vec<Ty<'tcx>> = fn_sig
.skip_binder()
@@ -899,33 +928,43 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
.map(|ty| transform_ty(tcx, *ty, options))
.collect();
let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
- ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
- tcx.mk_fn_sig(
- parameters,
- output,
- fn_sig.c_variadic(),
- fn_sig.unsafety(),
- fn_sig.abi(),
+ ty = Ty::new_fn_ptr(
+ tcx,
+ ty::Binder::bind_with_vars(
+ tcx.mk_fn_sig(
+ parameters,
+ output,
+ fn_sig.c_variadic(),
+ fn_sig.unsafety(),
+ fn_sig.abi(),
+ ),
+ fn_sig.bound_vars(),
),
- fn_sig.bound_vars(),
- ));
+ );
}
}
ty::Dynamic(predicates, _region, kind) => {
- ty = tcx.mk_dynamic(
+ ty = Ty::new_dynamic(
+ tcx,
transform_predicates(tcx, predicates, options),
tcx.lifetimes.re_erased,
*kind,
);
}
+ ty::Alias(..) => {
+ ty = transform_ty(
+ tcx,
+ tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
+ options,
+ );
+ }
+
ty::Bound(..)
| ty::Error(..)
- | ty::GeneratorWitness(..)
| ty::GeneratorWitnessMIR(..)
| ty::Infer(..)
- | ty::Alias(..)
| ty::Param(..)
| ty::Placeholder(..) => {
bug!("transform_ty: unexpected `{:?}`", ty.kind());
@@ -1076,14 +1115,16 @@ pub fn typeid_for_instance<'tcx>(
)]);
// Is the concrete self mutable?
let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
- tcx.mk_mut_ref(
+ Ty::new_mut_ref(
+ tcx,
tcx.lifetimes.re_erased,
- tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+ Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
)
} else {
- tcx.mk_imm_ref(
+ Ty::new_imm_ref(
+ tcx,
tcx.lifetimes.re_erased,
- tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+ Ty::new_dynamic(tcx, existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
)
};
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 4cccc6398..5e5cc6e4e 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -274,7 +274,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
let mut param_env = self.tcx.param_env_reveal_all_normalized(impl_def_id);
if !substs.is_empty() {
- param_env = EarlyBinder(param_env).subst(self.tcx, substs);
+ param_env = EarlyBinder::bind(param_env).subst(self.tcx, substs);
}
match &mut impl_trait_ref {
@@ -483,6 +483,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
}
ty::Alias(ty::Inherent, _) => bug!("symbol_names: unexpected inherent projection"),
+ ty::Alias(ty::Weak, _) => bug!("symbol_names: unexpected weak projection"),
ty::GeneratorWitness(_) => bug!("symbol_names: unexpected `GeneratorWitness`"),
ty::GeneratorWitnessMIR(..) => bug!("symbol_names: unexpected `GeneratorWitnessMIR`"),
}
@@ -534,7 +535,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
match predicate.as_ref().skip_binder() {
ty::ExistentialPredicate::Trait(trait_ref) => {
// Use a type that can't appear in defaults of type parameters.
- let dummy_self = cx.tcx.mk_fresh_ty(0);
+ let dummy_self = Ty::new_fresh(cx.tcx, 0);
let trait_ref = trait_ref.with_self_ty(cx.tcx, dummy_self);
cx = cx.print_def_path(trait_ref.def_id, trait_ref.substs)?;
}
@@ -650,7 +651,8 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
.builtin_deref(true)
.expect("tried to dereference on non-ptr type")
.ty;
- let dereferenced_const = self.tcx.mk_const(ct.kind(), pointee_ty);
+ // FIXME(const_generics): add an assert that we only do this for valtrees.
+ let dereferenced_const = self.tcx.mk_ct_from_kind(ct.kind(), pointee_ty);
self = dereferenced_const.print(self)?;
}
}
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index dff22fad4..a71e2e8cc 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -14,3 +14,8 @@ rustc_feature = { path = "../rustc_feature" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_span = { path = "../rustc_span" }
+
+[dependencies.object]
+version = "0.31.1"
+default-features = false
+features = ["elf"]
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 1ae11f567..c4abf6f4b 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -2,7 +2,6 @@ use crate::abi::{self, Abi, Align, FieldsShape, Size};
use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec};
use rustc_span::Symbol;
-use std::fmt;
use std::str::FromStr;
mod aarch64;
@@ -633,16 +632,6 @@ pub enum AdjustForForeignAbiError {
Unsupported { arch: Symbol, abi: spec::abi::Abi },
}
-impl fmt::Display for AdjustForForeignAbiError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Unsupported { arch, abi } => {
- write!(f, "target architecture {arch:?} does not support `extern {abi}` ABI")
- }
- }
- }
-}
-
impl<'a, Ty> FnAbi<'a, Ty> {
pub fn adjust_for_foreign_abi<C>(
&mut self,
diff --git a/compiler/rustc_target/src/abi/call/x86_64.rs b/compiler/rustc_target/src/abi/call/x86_64.rs
index 9427f27d1..b1aefaf05 100644
--- a/compiler/rustc_target/src/abi/call/x86_64.rs
+++ b/compiler/rustc_target/src/abi/call/x86_64.rs
@@ -1,5 +1,5 @@
// The classification code for the x86_64 ABI is taken from the clay language
-// https://github.com/jckarter/clay/blob/master/compiler/src/externals.cpp
+// https://github.com/jckarter/clay/blob/db0bd2702ab0b6e48965cd85f8859bbd5f60e48e/compiler/externals.cpp
use crate::abi::call::{ArgAbi, CastTarget, FnAbi, Reg, RegKind};
use crate::abi::{self, Abi, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
@@ -153,9 +153,9 @@ fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg
}
}
-fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
+fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
let mut i = 0;
- let lo = reg_component(cls, &mut i, size).unwrap();
+ let lo = reg_component(cls, &mut i, size)?;
let offset = Size::from_bytes(8) * (i as u64);
let mut target = CastTarget::from(lo);
if size > offset {
@@ -164,7 +164,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
}
}
assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
- target
+ Some(target)
}
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
@@ -227,7 +227,9 @@ where
// split into sized chunks passed individually
if arg.layout.is_aggregate() {
let size = arg.layout.size;
- arg.cast_to(cast_target(cls, size))
+ if let Some(cast_target) = cast_target(cls, size) {
+ arg.cast_to(cast_target);
+ }
} else {
arg.extend_integer_width_to(32);
}
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index dc2cc23ff..a7b54766b 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -11,6 +11,7 @@
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(exhaustive_patterns)]
+#![feature(iter_intersperse)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(rustc_attrs)]
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
index bb7c39ff2..f7cdfa71c 100644
--- a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs
@@ -1,10 +1,10 @@
-use super::apple_base::{opts, Arch};
+use super::apple_base::{opts, tvos_llvm_target, Arch};
use crate::spec::{FramePointer, Target, TargetOptions};
pub fn target() -> Target {
let arch = Arch::Arm64;
Target {
- llvm_target: "arm64-apple-tvos".into(),
+ llvm_target: tvos_llvm_target(arch).into(),
pointer_width: 64,
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
arch: arch.target_arch(),
diff --git a/compiler/rustc_target/src/spec/aarch64_be_unknown_netbsd.rs b/compiler/rustc_target/src/spec/aarch64_be_unknown_netbsd.rs
new file mode 100644
index 000000000..98ae05974
--- /dev/null
+++ b/compiler/rustc_target/src/spec/aarch64_be_unknown_netbsd.rs
@@ -0,0 +1,17 @@
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "aarch64_be-unknown-netbsd".into(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(),
+ arch: "aarch64".into(),
+ options: TargetOptions {
+ mcount: "__mcount".into(),
+ max_atomic_width: Some(128),
+ endian: Endian::Big,
+ ..super::netbsd_base::opts()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/apple/tests.rs b/compiler/rustc_target/src/spec/apple/tests.rs
index 3c90a5e7e..3b23ddadc 100644
--- a/compiler/rustc_target/src/spec/apple/tests.rs
+++ b/compiler/rustc_target/src/spec/apple/tests.rs
@@ -30,6 +30,9 @@ fn macos_link_environment_unmodified() {
for target in all_macos_targets {
// macOS targets should only remove information for cross-compiling, but never
// for the host.
- assert_eq!(target.link_env_remove, crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET"]);
+ assert_eq!(
+ target.link_env_remove,
+ crate::spec::cvs!["IPHONEOS_DEPLOYMENT_TARGET", "TVOS_DEPLOYMENT_TARGET"],
+ );
}
}
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index ff2246318..8a8d1ab95 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -240,7 +240,12 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
// Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
// may occur when we're linking a custom build script while targeting iOS for example.
if let Ok(sdkroot) = env::var("SDKROOT") {
- if sdkroot.contains("iPhoneOS.platform") || sdkroot.contains("iPhoneSimulator.platform")
+ if sdkroot.contains("iPhoneOS.platform")
+ || sdkroot.contains("iPhoneSimulator.platform")
+ || sdkroot.contains("AppleTVOS.platform")
+ || sdkroot.contains("AppleTVSimulator.platform")
+ || sdkroot.contains("WatchOS.platform")
+ || sdkroot.contains("WatchSimulator.platform")
{
env_remove.push("SDKROOT".into())
}
@@ -249,6 +254,7 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
// "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
// although this is apparently ignored when using the linker at "/usr/bin/ld".
env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
+ env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
env_remove.into()
} else {
// Otherwise if cross-compiling for a different OS/SDK, remove any part
@@ -299,6 +305,16 @@ fn tvos_lld_platform_version() -> String {
format!("{major}.{minor}")
}
+pub fn tvos_llvm_target(arch: Arch) -> String {
+ let (major, minor) = tvos_deployment_target();
+ format!("{}-apple-tvos{}.{}.0", arch.target_name(), major, minor)
+}
+
+pub fn tvos_sim_llvm_target(arch: Arch) -> String {
+ let (major, minor) = tvos_deployment_target();
+ format!("{}-apple-tvos{}.{}.0-simulator", arch.target_name(), major, minor)
+}
+
fn watchos_deployment_target() -> (u32, u32) {
// If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
diff --git a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs
index e2c0808f1..62c936036 100644
--- a/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs
+++ b/compiler/rustc_target/src/spec/armv7_sony_vita_newlibeabihf.rs
@@ -1,15 +1,18 @@
use crate::abi::Endian;
-use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions};
+use crate::spec::{cvs, Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions};
/// A base target for PlayStation Vita devices using the VITASDK toolchain (using newlib).
///
/// Requires the VITASDK toolchain on the host system.
pub fn target() -> Target {
- let pre_link_args = TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-Wl,-q"]);
+ let pre_link_args = TargetOptions::link_args(
+ LinkerFlavor::Gnu(Cc::Yes, Lld::No),
+ &["-Wl,-q", "-Wl,--pic-veneer"],
+ );
Target {
- llvm_target: "armv7a-vita-eabihf".into(),
+ llvm_target: "thumbv7a-vita-eabihf".into(),
pointer_width: 32,
data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),
arch: "arm".into(),
@@ -18,21 +21,19 @@ pub fn target() -> Target {
os: "vita".into(),
endian: Endian::Little,
c_int_width: "32".into(),
- dynamic_linking: false,
env: "newlib".into(),
vendor: "sony".into(),
abi: "eabihf".into(),
linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No),
no_default_libraries: false,
cpu: "cortex-a9".into(),
- executables: true,
families: cvs!["unix"],
linker: Some("arm-vita-eabi-gcc".into()),
relocation_model: RelocModel::Static,
- features: "+v7,+neon".into(),
+ features: "+v7,+neon,+vfp3,+thumb2,+thumb-mode".into(),
pre_link_args,
exe_suffix: ".elf".into(),
- panic_strategy: PanicStrategy::Abort,
+ has_thumb_interworking: true,
max_atomic_width: Some(64),
..Default::default()
},
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index 9c3406b53..fbec44b71 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -1,4 +1,5 @@
use crate::spec::{Cc, LinkerFlavor, Lld, RelocModel, Target, TargetOptions};
+use object::elf;
/// A base target for AVR devices using the GNU toolchain.
///
@@ -29,3 +30,339 @@ pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
},
}
}
+
+/// Resolve the value of the EF_AVR_ARCH field for AVR ELF files, given the
+/// name of the target CPU / MCU.
+///
+/// In ELF files using the AVR architecture, the lower 7 bits of the e_flags
+/// field is a code that identifies the "ISA revision" of the object code.
+///
+/// This flag is generally set by AVR compilers in their output ELF files,
+/// and linkers like avr-ld check this flag in all of their input files to
+/// make sure they are compiled with the same ISA revision.
+pub fn ef_avr_arch(target_cpu: &str) -> u32 {
+ // Adapted from llvm-project/llvm/lib/target/AVR/AVRDevices.td
+ match target_cpu {
+ // Generic MCUs
+ "avr1" => elf::EF_AVR_ARCH_AVR1,
+ "avr2" => elf::EF_AVR_ARCH_AVR2,
+ "avr25" => elf::EF_AVR_ARCH_AVR25,
+ "avr3" => elf::EF_AVR_ARCH_AVR3,
+ "avr31" => elf::EF_AVR_ARCH_AVR31,
+ "avr35" => elf::EF_AVR_ARCH_AVR35,
+ "avr4" => elf::EF_AVR_ARCH_AVR4,
+ "avr5" => elf::EF_AVR_ARCH_AVR5,
+ "avr51" => elf::EF_AVR_ARCH_AVR51,
+ "avr6" => elf::EF_AVR_ARCH_AVR6,
+ "avrxmega1" => elf::EF_AVR_ARCH_XMEGA1,
+ "avrxmega2" => elf::EF_AVR_ARCH_XMEGA2,
+ "avrxmega3" => elf::EF_AVR_ARCH_XMEGA3,
+ "avrxmega4" => elf::EF_AVR_ARCH_XMEGA4,
+ "avrxmega5" => elf::EF_AVR_ARCH_XMEGA5,
+ "avrxmega6" => elf::EF_AVR_ARCH_XMEGA6,
+ "avrxmega7" => elf::EF_AVR_ARCH_XMEGA7,
+ "avrtiny" => elf::EF_AVR_ARCH_AVRTINY,
+
+ // Specific MCUs
+ "at90s1200" => elf::EF_AVR_ARCH_AVR1,
+ "attiny11" => elf::EF_AVR_ARCH_AVR1,
+ "attiny12" => elf::EF_AVR_ARCH_AVR1,
+ "attiny15" => elf::EF_AVR_ARCH_AVR1,
+ "attiny28" => elf::EF_AVR_ARCH_AVR1,
+ "at90s2313" => elf::EF_AVR_ARCH_AVR2,
+ "at90s2323" => elf::EF_AVR_ARCH_AVR2,
+ "at90s2333" => elf::EF_AVR_ARCH_AVR2,
+ "at90s2343" => elf::EF_AVR_ARCH_AVR2,
+ "attiny22" => elf::EF_AVR_ARCH_AVR2,
+ "attiny26" => elf::EF_AVR_ARCH_AVR2,
+ "at86rf401" => elf::EF_AVR_ARCH_AVR25,
+ "at90s4414" => elf::EF_AVR_ARCH_AVR2,
+ "at90s4433" => elf::EF_AVR_ARCH_AVR2,
+ "at90s4434" => elf::EF_AVR_ARCH_AVR2,
+ "at90s8515" => elf::EF_AVR_ARCH_AVR2,
+ "at90c8534" => elf::EF_AVR_ARCH_AVR2,
+ "at90s8535" => elf::EF_AVR_ARCH_AVR2,
+ "ata5272" => elf::EF_AVR_ARCH_AVR25,
+ "ata6616c" => elf::EF_AVR_ARCH_AVR25,
+ "attiny13" => elf::EF_AVR_ARCH_AVR25,
+ "attiny13a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny2313" => elf::EF_AVR_ARCH_AVR25,
+ "attiny2313a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny24" => elf::EF_AVR_ARCH_AVR25,
+ "attiny24a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny4313" => elf::EF_AVR_ARCH_AVR25,
+ "attiny44" => elf::EF_AVR_ARCH_AVR25,
+ "attiny44a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny84" => elf::EF_AVR_ARCH_AVR25,
+ "attiny84a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny25" => elf::EF_AVR_ARCH_AVR25,
+ "attiny45" => elf::EF_AVR_ARCH_AVR25,
+ "attiny85" => elf::EF_AVR_ARCH_AVR25,
+ "attiny261" => elf::EF_AVR_ARCH_AVR25,
+ "attiny261a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny441" => elf::EF_AVR_ARCH_AVR25,
+ "attiny461" => elf::EF_AVR_ARCH_AVR25,
+ "attiny461a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny841" => elf::EF_AVR_ARCH_AVR25,
+ "attiny861" => elf::EF_AVR_ARCH_AVR25,
+ "attiny861a" => elf::EF_AVR_ARCH_AVR25,
+ "attiny87" => elf::EF_AVR_ARCH_AVR25,
+ "attiny43u" => elf::EF_AVR_ARCH_AVR25,
+ "attiny48" => elf::EF_AVR_ARCH_AVR25,
+ "attiny88" => elf::EF_AVR_ARCH_AVR25,
+ "attiny828" => elf::EF_AVR_ARCH_AVR25,
+ "at43usb355" => elf::EF_AVR_ARCH_AVR3,
+ "at76c711" => elf::EF_AVR_ARCH_AVR3,
+ "atmega103" => elf::EF_AVR_ARCH_AVR31,
+ "at43usb320" => elf::EF_AVR_ARCH_AVR31,
+ "attiny167" => elf::EF_AVR_ARCH_AVR35,
+ "at90usb82" => elf::EF_AVR_ARCH_AVR35,
+ "at90usb162" => elf::EF_AVR_ARCH_AVR35,
+ "ata5505" => elf::EF_AVR_ARCH_AVR35,
+ "ata6617c" => elf::EF_AVR_ARCH_AVR35,
+ "ata664251" => elf::EF_AVR_ARCH_AVR35,
+ "atmega8u2" => elf::EF_AVR_ARCH_AVR35,
+ "atmega16u2" => elf::EF_AVR_ARCH_AVR35,
+ "atmega32u2" => elf::EF_AVR_ARCH_AVR35,
+ "attiny1634" => elf::EF_AVR_ARCH_AVR35,
+ "atmega8" => elf::EF_AVR_ARCH_AVR4,
+ "ata6289" => elf::EF_AVR_ARCH_AVR4,
+ "atmega8a" => elf::EF_AVR_ARCH_AVR4,
+ "ata6285" => elf::EF_AVR_ARCH_AVR4,
+ "ata6286" => elf::EF_AVR_ARCH_AVR4,
+ "ata6612c" => elf::EF_AVR_ARCH_AVR4,
+ "atmega48" => elf::EF_AVR_ARCH_AVR4,
+ "atmega48a" => elf::EF_AVR_ARCH_AVR4,
+ "atmega48pa" => elf::EF_AVR_ARCH_AVR4,
+ "atmega48pb" => elf::EF_AVR_ARCH_AVR4,
+ "atmega48p" => elf::EF_AVR_ARCH_AVR4,
+ "atmega88" => elf::EF_AVR_ARCH_AVR4,
+ "atmega88a" => elf::EF_AVR_ARCH_AVR4,
+ "atmega88p" => elf::EF_AVR_ARCH_AVR4,
+ "atmega88pa" => elf::EF_AVR_ARCH_AVR4,
+ "atmega88pb" => elf::EF_AVR_ARCH_AVR4,
+ "atmega8515" => elf::EF_AVR_ARCH_AVR4,
+ "atmega8535" => elf::EF_AVR_ARCH_AVR4,
+ "atmega8hva" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm1" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm2" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm2b" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm3" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm3b" => elf::EF_AVR_ARCH_AVR4,
+ "at90pwm81" => elf::EF_AVR_ARCH_AVR4,
+ "ata5702m322" => elf::EF_AVR_ARCH_AVR5,
+ "ata5782" => elf::EF_AVR_ARCH_AVR5,
+ "ata5790" => elf::EF_AVR_ARCH_AVR5,
+ "ata5790n" => elf::EF_AVR_ARCH_AVR5,
+ "ata5791" => elf::EF_AVR_ARCH_AVR5,
+ "ata5795" => elf::EF_AVR_ARCH_AVR5,
+ "ata5831" => elf::EF_AVR_ARCH_AVR5,
+ "ata6613c" => elf::EF_AVR_ARCH_AVR5,
+ "ata6614q" => elf::EF_AVR_ARCH_AVR5,
+ "ata8210" => elf::EF_AVR_ARCH_AVR5,
+ "ata8510" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega161" => elf::EF_AVR_ARCH_AVR5,
+ "atmega162" => elf::EF_AVR_ARCH_AVR5,
+ "atmega163" => elf::EF_AVR_ARCH_AVR5,
+ "atmega164a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega164p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega164pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega165" => elf::EF_AVR_ARCH_AVR5,
+ "atmega165a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega165p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega165pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega168" => elf::EF_AVR_ARCH_AVR5,
+ "atmega168a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega168p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega168pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega168pb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega169" => elf::EF_AVR_ARCH_AVR5,
+ "atmega169a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega169p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega169pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega323" => elf::EF_AVR_ARCH_AVR5,
+ "atmega324a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega324p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega324pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega324pb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega325" => elf::EF_AVR_ARCH_AVR5,
+ "atmega325a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega325p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega325pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3250" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3250a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3250p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3250pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega328" => elf::EF_AVR_ARCH_AVR5,
+ "atmega328p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega328pb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega329" => elf::EF_AVR_ARCH_AVR5,
+ "atmega329a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega329p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega329pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3290" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3290a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3290p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega3290pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega406" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega640" => elf::EF_AVR_ARCH_AVR5,
+ "atmega644" => elf::EF_AVR_ARCH_AVR5,
+ "atmega644a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega644p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega644pa" => elf::EF_AVR_ARCH_AVR5,
+ "atmega645" => elf::EF_AVR_ARCH_AVR5,
+ "atmega645a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega645p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega649" => elf::EF_AVR_ARCH_AVR5,
+ "atmega649a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega649p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6450" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6450a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6450p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6490" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6490a" => elf::EF_AVR_ARCH_AVR5,
+ "atmega6490p" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64rfr2" => elf::EF_AVR_ARCH_AVR5,
+ "atmega644rfr2" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16hva" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16hva2" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16hvb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16hvbrevb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32hvb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32hvbrevb" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64hve" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64hve2" => elf::EF_AVR_ARCH_AVR5,
+ "at90can32" => elf::EF_AVR_ARCH_AVR5,
+ "at90can64" => elf::EF_AVR_ARCH_AVR5,
+ "at90pwm161" => elf::EF_AVR_ARCH_AVR5,
+ "at90pwm216" => elf::EF_AVR_ARCH_AVR5,
+ "at90pwm316" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32c1" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64c1" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16m1" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32m1" => elf::EF_AVR_ARCH_AVR5,
+ "atmega64m1" => elf::EF_AVR_ARCH_AVR5,
+ "atmega16u4" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32u4" => elf::EF_AVR_ARCH_AVR5,
+ "atmega32u6" => elf::EF_AVR_ARCH_AVR5,
+ "at90usb646" => elf::EF_AVR_ARCH_AVR5,
+ "at90usb647" => elf::EF_AVR_ARCH_AVR5,
+ "at90scr100" => elf::EF_AVR_ARCH_AVR5,
+ "at94k" => elf::EF_AVR_ARCH_AVR5,
+ "m3000" => elf::EF_AVR_ARCH_AVR5,
+ "atmega128" => elf::EF_AVR_ARCH_AVR51,
+ "atmega128a" => elf::EF_AVR_ARCH_AVR51,
+ "atmega1280" => elf::EF_AVR_ARCH_AVR51,
+ "atmega1281" => elf::EF_AVR_ARCH_AVR51,
+ "atmega1284" => elf::EF_AVR_ARCH_AVR51,
+ "atmega1284p" => elf::EF_AVR_ARCH_AVR51,
+ "atmega128rfa1" => elf::EF_AVR_ARCH_AVR51,
+ "atmega128rfr2" => elf::EF_AVR_ARCH_AVR51,
+ "atmega1284rfr2" => elf::EF_AVR_ARCH_AVR51,
+ "at90can128" => elf::EF_AVR_ARCH_AVR51,
+ "at90usb1286" => elf::EF_AVR_ARCH_AVR51,
+ "at90usb1287" => elf::EF_AVR_ARCH_AVR51,
+ "atmega2560" => elf::EF_AVR_ARCH_AVR6,
+ "atmega2561" => elf::EF_AVR_ARCH_AVR6,
+ "atmega256rfr2" => elf::EF_AVR_ARCH_AVR6,
+ "atmega2564rfr2" => elf::EF_AVR_ARCH_AVR6,
+ "atxmega16a4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega16a4u" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega16c4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega16d4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32a4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32a4u" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32c3" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32c4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32d3" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32d4" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega32e5" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega16e5" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega8e5" => elf::EF_AVR_ARCH_XMEGA2,
+ "atxmega64a3" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64a3u" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64a4u" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64b1" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64b3" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64c3" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64d3" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64d4" => elf::EF_AVR_ARCH_XMEGA4,
+ "atxmega64a1" => elf::EF_AVR_ARCH_XMEGA5,
+ "atxmega64a1u" => elf::EF_AVR_ARCH_XMEGA5,
+ "atxmega128a3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128a3u" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128b1" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128b3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128c3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128d3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128d4" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega192a3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega192a3u" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega192c3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega192d3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256a3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256a3u" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256a3b" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256a3bu" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256c3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega256d3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega384c3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega384d3" => elf::EF_AVR_ARCH_XMEGA6,
+ "atxmega128a1" => elf::EF_AVR_ARCH_XMEGA7,
+ "atxmega128a1u" => elf::EF_AVR_ARCH_XMEGA7,
+ "atxmega128a4u" => elf::EF_AVR_ARCH_XMEGA7,
+ "attiny4" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny5" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny9" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny10" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny20" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny40" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny102" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny104" => elf::EF_AVR_ARCH_AVRTINY,
+ "attiny202" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny402" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny204" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny404" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny804" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1604" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny406" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny806" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1606" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny807" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1607" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny212" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny412" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny214" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny414" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny814" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1614" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny416" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny816" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1616" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny3216" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny417" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny817" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1617" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny3217" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1624" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1626" => elf::EF_AVR_ARCH_XMEGA3,
+ "attiny1627" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega808" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega809" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega1608" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega1609" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega3208" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega3209" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega4808" => elf::EF_AVR_ARCH_XMEGA3,
+ "atmega4809" => elf::EF_AVR_ARCH_XMEGA3,
+
+ // Unknown target CPU => Unspecified/generic code
+ _ => 0,
+ }
+}
diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs
new file mode 100644
index 000000000..209d481d6
--- /dev/null
+++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none.rs
@@ -0,0 +1,22 @@
+use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel};
+use super::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "loongarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "loongarch64".into(),
+ options: TargetOptions {
+ cpu: "generic".into(),
+ features: "+f,+d".into(),
+ linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ code_model: Some(CodeModel::Small),
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs
new file mode 100644
index 000000000..f444a7f24
--- /dev/null
+++ b/compiler/rustc_target/src/spec/loongarch64_unknown_none_softfloat.rs
@@ -0,0 +1,23 @@
+use super::{Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy, RelocModel};
+use super::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "loongarch64-unknown-none".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+ arch: "loongarch64".into(),
+ options: TargetOptions {
+ cpu: "generic".into(),
+ features: "-f,-d".into(),
+ abi: "softfloat".into(),
+ linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::No),
+ llvm_abiname: "lp64s".into(),
+ max_atomic_width: Some(64),
+ relocation_model: RelocModel::Static,
+ panic_strategy: PanicStrategy::Abort,
+ code_model: Some(CodeModel::Small),
+ ..Default::default()
+ },
+ }
+}
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 fc5dbd114..b9df0046b 100644
--- a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs
+++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs
@@ -12,7 +12,7 @@ pub fn target() -> Target {
endian: Endian::Big,
// NOTE(mips64r2) matches C toolchain
cpu: "mips64r2".into(),
- features: "+mips64r2".into(),
+ features: "+mips64r2,+xgot".into(),
max_atomic_width: Some(64),
mcount: "_mcount".into(),
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 e0d5f6f57..57ad8c473 100644
--- a/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs
+++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs
@@ -10,7 +10,7 @@ pub fn target() -> Target {
abi: "abi64".into(),
// NOTE(mips64r2) matches C toolchain
cpu: "mips64r2".into(),
- features: "+mips64r2".into(),
+ features: "+mips64r2,+xgot".into(),
max_atomic_width: Some(64),
mcount: "_mcount".into(),
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index ba4b89c9e..2365dfaf1 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -62,6 +62,7 @@ mod android_base;
mod apple_base;
pub use apple_base::deployment_target as current_apple_deployment_target;
mod avr_gnu_base;
+pub use avr_gnu_base::ef_avr_arch;
mod bpf_base;
mod dragonfly_base;
mod freebsd_base;
@@ -160,15 +161,49 @@ pub enum LinkerFlavor {
/// linker flavors (`LinkerFlavor`).
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LinkerFlavorCli {
+ // New (unstable) flavors, with direct counterparts in `LinkerFlavor`.
+ Gnu(Cc, Lld),
+ Darwin(Cc, Lld),
+ WasmLld(Cc),
+ Unix(Cc),
+ // Note: `Msvc(Lld::No)` is also a stable value.
+ Msvc(Lld),
+ EmCc,
+ Bpf,
+ Ptx,
+
+ // Below: the legacy stable values.
Gcc,
Ld,
Lld(LldFlavor),
- Msvc,
Em,
BpfLinker,
PtxLinker,
}
+impl LinkerFlavorCli {
+ /// Returns whether this `-C linker-flavor` option is one of the unstable values.
+ pub fn is_unstable(&self) -> bool {
+ match self {
+ LinkerFlavorCli::Gnu(..)
+ | LinkerFlavorCli::Darwin(..)
+ | LinkerFlavorCli::WasmLld(..)
+ | LinkerFlavorCli::Unix(..)
+ | LinkerFlavorCli::Msvc(Lld::Yes)
+ | LinkerFlavorCli::EmCc
+ | LinkerFlavorCli::Bpf
+ | LinkerFlavorCli::Ptx
+ | LinkerFlavorCli::BpfLinker
+ | LinkerFlavorCli::PtxLinker => true,
+ LinkerFlavorCli::Gcc
+ | LinkerFlavorCli::Ld
+ | LinkerFlavorCli::Lld(..)
+ | LinkerFlavorCli::Msvc(Lld::No)
+ | LinkerFlavorCli::Em => false,
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum LldFlavor {
Wasm,
@@ -205,16 +240,22 @@ impl ToJson for LldFlavor {
}
impl LinkerFlavor {
- pub fn from_cli(cli: LinkerFlavorCli, target: &TargetOptions) -> LinkerFlavor {
- Self::from_cli_impl(cli, target.linker_flavor.lld_flavor(), target.linker_flavor.is_gnu())
- }
-
- /// The passed CLI flavor is preferred over other args coming from the default target spec,
- /// so this function can produce a flavor that is incompatible with the current target.
- /// FIXME: Produce errors when `-Clinker-flavor` is set to something incompatible
- /// with the current target.
- fn from_cli_impl(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
+ /// At this point the target's reference linker flavor doesn't yet exist and we need to infer
+ /// it. The inference always succeds and gives some result, and we don't report any flavor
+ /// incompatibility errors for json target specs. The CLI flavor is used as the main source
+ /// of truth, other flags are used in case of ambiguities.
+ fn from_cli_json(cli: LinkerFlavorCli, lld_flavor: LldFlavor, is_gnu: bool) -> LinkerFlavor {
match cli {
+ LinkerFlavorCli::Gnu(cc, lld) => LinkerFlavor::Gnu(cc, lld),
+ LinkerFlavorCli::Darwin(cc, lld) => LinkerFlavor::Darwin(cc, lld),
+ LinkerFlavorCli::WasmLld(cc) => LinkerFlavor::WasmLld(cc),
+ LinkerFlavorCli::Unix(cc) => LinkerFlavor::Unix(cc),
+ LinkerFlavorCli::Msvc(lld) => LinkerFlavor::Msvc(lld),
+ LinkerFlavorCli::EmCc => LinkerFlavor::EmCc,
+ LinkerFlavorCli::Bpf => LinkerFlavor::Bpf,
+ LinkerFlavorCli::Ptx => LinkerFlavor::Ptx,
+
+ // Below: legacy stable values
LinkerFlavorCli::Gcc => match lld_flavor {
LldFlavor::Ld if is_gnu => LinkerFlavor::Gnu(Cc::Yes, Lld::No),
LldFlavor::Ld64 => LinkerFlavor::Darwin(Cc::Yes, Lld::No),
@@ -230,7 +271,6 @@ impl LinkerFlavor {
LinkerFlavorCli::Lld(LldFlavor::Ld64) => LinkerFlavor::Darwin(Cc::No, Lld::Yes),
LinkerFlavorCli::Lld(LldFlavor::Wasm) => LinkerFlavor::WasmLld(Cc::No),
LinkerFlavorCli::Lld(LldFlavor::Link) => LinkerFlavor::Msvc(Lld::Yes),
- LinkerFlavorCli::Msvc => LinkerFlavor::Msvc(Lld::No),
LinkerFlavorCli::Em => LinkerFlavor::EmCc,
LinkerFlavorCli::BpfLinker => LinkerFlavor::Bpf,
LinkerFlavorCli::PtxLinker => LinkerFlavor::Ptx,
@@ -250,13 +290,119 @@ impl LinkerFlavor {
LinkerFlavorCli::Ld
}
LinkerFlavor::Msvc(Lld::Yes) => LinkerFlavorCli::Lld(LldFlavor::Link),
- LinkerFlavor::Msvc(..) => LinkerFlavorCli::Msvc,
+ LinkerFlavor::Msvc(..) => LinkerFlavorCli::Msvc(Lld::No),
LinkerFlavor::EmCc => LinkerFlavorCli::Em,
LinkerFlavor::Bpf => LinkerFlavorCli::BpfLinker,
LinkerFlavor::Ptx => LinkerFlavorCli::PtxLinker,
}
}
+ fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option<Cc>, Option<Lld>) {
+ match cli {
+ LinkerFlavorCli::Gnu(cc, lld) | LinkerFlavorCli::Darwin(cc, lld) => {
+ (Some(cc), Some(lld))
+ }
+ LinkerFlavorCli::WasmLld(cc) => (Some(cc), Some(Lld::Yes)),
+ LinkerFlavorCli::Unix(cc) => (Some(cc), None),
+ LinkerFlavorCli::Msvc(lld) => (Some(Cc::No), Some(lld)),
+ LinkerFlavorCli::EmCc => (Some(Cc::Yes), Some(Lld::Yes)),
+ LinkerFlavorCli::Bpf | LinkerFlavorCli::Ptx => (None, None),
+
+ // Below: legacy stable values
+ LinkerFlavorCli::Gcc => (Some(Cc::Yes), None),
+ LinkerFlavorCli::Ld => (Some(Cc::No), Some(Lld::No)),
+ LinkerFlavorCli::Lld(_) => (Some(Cc::No), Some(Lld::Yes)),
+ LinkerFlavorCli::Em => (Some(Cc::Yes), Some(Lld::Yes)),
+ LinkerFlavorCli::BpfLinker | LinkerFlavorCli::PtxLinker => (None, None),
+ }
+ }
+
+ fn infer_linker_hints(linker_stem: &str) -> (Option<Cc>, Option<Lld>) {
+ // Remove any version postfix.
+ let stem = linker_stem
+ .rsplit_once('-')
+ .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
+ .unwrap_or(linker_stem);
+
+ // GCC/Clang can have an optional target prefix.
+ if stem == "emcc"
+ || stem == "gcc"
+ || stem.ends_with("-gcc")
+ || stem == "g++"
+ || stem.ends_with("-g++")
+ || stem == "clang"
+ || stem.ends_with("-clang")
+ || stem == "clang++"
+ || stem.ends_with("-clang++")
+ {
+ (Some(Cc::Yes), None)
+ } else if stem == "wasm-ld"
+ || stem.ends_with("-wasm-ld")
+ || stem == "ld.lld"
+ || stem == "lld"
+ || stem == "rust-lld"
+ || stem == "lld-link"
+ {
+ (Some(Cc::No), Some(Lld::Yes))
+ } else if stem == "ld" || stem.ends_with("-ld") || stem == "link" {
+ (Some(Cc::No), Some(Lld::No))
+ } else {
+ (None, None)
+ }
+ }
+
+ fn with_hints(self, (cc_hint, lld_hint): (Option<Cc>, Option<Lld>)) -> LinkerFlavor {
+ match self {
+ LinkerFlavor::Gnu(cc, lld) => {
+ LinkerFlavor::Gnu(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
+ }
+ LinkerFlavor::Darwin(cc, lld) => {
+ LinkerFlavor::Darwin(cc_hint.unwrap_or(cc), lld_hint.unwrap_or(lld))
+ }
+ LinkerFlavor::WasmLld(cc) => LinkerFlavor::WasmLld(cc_hint.unwrap_or(cc)),
+ LinkerFlavor::Unix(cc) => LinkerFlavor::Unix(cc_hint.unwrap_or(cc)),
+ LinkerFlavor::Msvc(lld) => LinkerFlavor::Msvc(lld_hint.unwrap_or(lld)),
+ LinkerFlavor::EmCc | LinkerFlavor::Bpf | LinkerFlavor::Ptx => self,
+ }
+ }
+
+ pub fn with_cli_hints(self, cli: LinkerFlavorCli) -> LinkerFlavor {
+ self.with_hints(LinkerFlavor::infer_cli_hints(cli))
+ }
+
+ pub fn with_linker_hints(self, linker_stem: &str) -> LinkerFlavor {
+ self.with_hints(LinkerFlavor::infer_linker_hints(linker_stem))
+ }
+
+ pub fn check_compatibility(self, cli: LinkerFlavorCli) -> Option<String> {
+ let compatible = |cli| {
+ // The CLI flavor should be compatible with the target if:
+ // 1. they are counterparts: they have the same principal flavor.
+ match (self, cli) {
+ (LinkerFlavor::Gnu(..), LinkerFlavorCli::Gnu(..))
+ | (LinkerFlavor::Darwin(..), LinkerFlavorCli::Darwin(..))
+ | (LinkerFlavor::WasmLld(..), LinkerFlavorCli::WasmLld(..))
+ | (LinkerFlavor::Unix(..), LinkerFlavorCli::Unix(..))
+ | (LinkerFlavor::Msvc(..), LinkerFlavorCli::Msvc(..))
+ | (LinkerFlavor::EmCc, LinkerFlavorCli::EmCc)
+ | (LinkerFlavor::Bpf, LinkerFlavorCli::Bpf)
+ | (LinkerFlavor::Ptx, LinkerFlavorCli::Ptx) => return true,
+ _ => {}
+ }
+
+ // 2. or, the flavor is legacy and survives this roundtrip.
+ cli == self.with_cli_hints(cli).to_cli()
+ };
+ (!compatible(cli)).then(|| {
+ LinkerFlavorCli::all()
+ .iter()
+ .filter(|cli| compatible(**cli))
+ .map(|cli| cli.desc())
+ .intersperse(", ")
+ .collect()
+ })
+ }
+
pub fn lld_flavor(self) -> LldFlavor {
match self {
LinkerFlavor::Gnu(..)
@@ -273,11 +419,52 @@ impl LinkerFlavor {
pub fn is_gnu(self) -> bool {
matches!(self, LinkerFlavor::Gnu(..))
}
+
+ /// Returns whether the flavor uses the `lld` linker.
+ pub fn uses_lld(self) -> bool {
+ // Exhaustive match in case new flavors are added in the future.
+ match self {
+ LinkerFlavor::Gnu(_, Lld::Yes)
+ | LinkerFlavor::Darwin(_, Lld::Yes)
+ | LinkerFlavor::WasmLld(..)
+ | LinkerFlavor::EmCc
+ | LinkerFlavor::Msvc(Lld::Yes) => true,
+ LinkerFlavor::Gnu(..)
+ | LinkerFlavor::Darwin(..)
+ | LinkerFlavor::Msvc(_)
+ | LinkerFlavor::Unix(_)
+ | LinkerFlavor::Bpf
+ | LinkerFlavor::Ptx => false,
+ }
+ }
+
+ /// Returns whether the flavor calls the linker via a C/C++ compiler.
+ pub fn uses_cc(self) -> bool {
+ // Exhaustive match in case new flavors are added in the future.
+ match self {
+ LinkerFlavor::Gnu(Cc::Yes, _)
+ | LinkerFlavor::Darwin(Cc::Yes, _)
+ | LinkerFlavor::WasmLld(Cc::Yes)
+ | LinkerFlavor::Unix(Cc::Yes)
+ | LinkerFlavor::EmCc => true,
+ LinkerFlavor::Gnu(..)
+ | LinkerFlavor::Darwin(..)
+ | LinkerFlavor::WasmLld(_)
+ | LinkerFlavor::Msvc(_)
+ | LinkerFlavor::Unix(_)
+ | LinkerFlavor::Bpf
+ | LinkerFlavor::Ptx => false,
+ }
+ }
}
macro_rules! linker_flavor_cli_impls {
($(($($flavor:tt)*) $string:literal)*) => (
impl LinkerFlavorCli {
+ const fn all() -> &'static [LinkerFlavorCli] {
+ &[$($($flavor)*,)*]
+ }
+
pub const fn one_of() -> &'static str {
concat!("one of: ", $($string, " ",)*)
}
@@ -289,8 +476,8 @@ macro_rules! linker_flavor_cli_impls {
})
}
- pub fn desc(&self) -> &str {
- match *self {
+ pub fn desc(self) -> &'static str {
+ match self {
$($($flavor)* => $string,)*
}
}
@@ -299,13 +486,31 @@ macro_rules! linker_flavor_cli_impls {
}
linker_flavor_cli_impls! {
+ (LinkerFlavorCli::Gnu(Cc::No, Lld::No)) "gnu"
+ (LinkerFlavorCli::Gnu(Cc::No, Lld::Yes)) "gnu-lld"
+ (LinkerFlavorCli::Gnu(Cc::Yes, Lld::No)) "gnu-cc"
+ (LinkerFlavorCli::Gnu(Cc::Yes, Lld::Yes)) "gnu-lld-cc"
+ (LinkerFlavorCli::Darwin(Cc::No, Lld::No)) "darwin"
+ (LinkerFlavorCli::Darwin(Cc::No, Lld::Yes)) "darwin-lld"
+ (LinkerFlavorCli::Darwin(Cc::Yes, Lld::No)) "darwin-cc"
+ (LinkerFlavorCli::Darwin(Cc::Yes, Lld::Yes)) "darwin-lld-cc"
+ (LinkerFlavorCli::WasmLld(Cc::No)) "wasm-lld"
+ (LinkerFlavorCli::WasmLld(Cc::Yes)) "wasm-lld-cc"
+ (LinkerFlavorCli::Unix(Cc::No)) "unix"
+ (LinkerFlavorCli::Unix(Cc::Yes)) "unix-cc"
+ (LinkerFlavorCli::Msvc(Lld::Yes)) "msvc-lld"
+ (LinkerFlavorCli::Msvc(Lld::No)) "msvc"
+ (LinkerFlavorCli::EmCc) "em-cc"
+ (LinkerFlavorCli::Bpf) "bpf"
+ (LinkerFlavorCli::Ptx) "ptx"
+
+ // Below: legacy stable values
(LinkerFlavorCli::Gcc) "gcc"
(LinkerFlavorCli::Ld) "ld"
(LinkerFlavorCli::Lld(LldFlavor::Ld)) "ld.lld"
(LinkerFlavorCli::Lld(LldFlavor::Ld64)) "ld64.lld"
(LinkerFlavorCli::Lld(LldFlavor::Link)) "lld-link"
(LinkerFlavorCli::Lld(LldFlavor::Wasm)) "wasm-ld"
- (LinkerFlavorCli::Msvc) "msvc"
(LinkerFlavorCli::Em) "em"
(LinkerFlavorCli::BpfLinker) "bpf-linker"
(LinkerFlavorCli::PtxLinker) "ptx-linker"
@@ -596,6 +801,17 @@ impl LinkOutputKind {
_ => return None,
})
}
+
+ pub fn can_link_dylib(self) -> bool {
+ match self {
+ LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false,
+ LinkOutputKind::DynamicNoPicExe
+ | LinkOutputKind::DynamicPicExe
+ | LinkOutputKind::DynamicDylib
+ | LinkOutputKind::StaticDylib
+ | LinkOutputKind::WasiReactorExe => true,
+ }
+ }
}
impl fmt::Display for LinkOutputKind {
@@ -815,6 +1031,7 @@ bitflags::bitflags! {
const SHADOWCALLSTACK = 1 << 7;
const KCFI = 1 << 8;
const KERNELADDRESS = 1 << 9;
+ const SAFESTACK = 1 << 10;
}
}
@@ -831,6 +1048,7 @@ impl SanitizerSet {
SanitizerSet::LEAK => "leak",
SanitizerSet::MEMORY => "memory",
SanitizerSet::MEMTAG => "memtag",
+ SanitizerSet::SAFESTACK => "safestack",
SanitizerSet::SHADOWCALLSTACK => "shadow-call-stack",
SanitizerSet::THREAD => "thread",
SanitizerSet::HWADDRESS => "hwaddress",
@@ -871,6 +1089,7 @@ impl IntoIterator for SanitizerSet {
SanitizerSet::THREAD,
SanitizerSet::HWADDRESS,
SanitizerSet::KERNELADDRESS,
+ SanitizerSet::SAFESTACK,
]
.iter()
.copied()
@@ -1101,10 +1320,12 @@ supported_targets! {
("x86_64-unknown-openbsd", x86_64_unknown_openbsd),
("aarch64-unknown-netbsd", aarch64_unknown_netbsd),
+ ("aarch64_be-unknown-netbsd", aarch64_be_unknown_netbsd),
("armv6-unknown-netbsd-eabihf", armv6_unknown_netbsd_eabihf),
("armv7-unknown-netbsd-eabihf", armv7_unknown_netbsd_eabihf),
("i686-unknown-netbsd", i686_unknown_netbsd),
("powerpc-unknown-netbsd", powerpc_unknown_netbsd),
+ ("riscv64gc-unknown-netbsd", riscv64gc_unknown_netbsd),
("sparc64-unknown-netbsd", sparc64_unknown_netbsd),
("x86_64-unknown-netbsd", x86_64_unknown_netbsd),
@@ -1202,6 +1423,7 @@ supported_targets! {
("riscv32im-unknown-none-elf", riscv32im_unknown_none_elf),
("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
("riscv32imc-esp-espidf", riscv32imc_esp_espidf),
+ ("riscv32imac-esp-espidf", riscv32imac_esp_espidf),
("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf),
("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf),
("riscv32gc-unknown-linux-gnu", riscv32gc_unknown_linux_gnu),
@@ -1211,6 +1433,9 @@ supported_targets! {
("riscv64gc-unknown-linux-gnu", riscv64gc_unknown_linux_gnu),
("riscv64gc-unknown-linux-musl", riscv64gc_unknown_linux_musl),
+ ("loongarch64-unknown-none", loongarch64_unknown_none),
+ ("loongarch64-unknown-none-softfloat", loongarch64_unknown_none_softfloat),
+
("aarch64-unknown-none", aarch64_unknown_none),
("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat),
@@ -1573,7 +1798,7 @@ pub struct TargetOptions {
pub static_position_independent_executables: bool,
/// Determines if the target always requires using the PLT for indirect
/// library calls or not. This controls the default value of the `-Z plt` flag.
- pub needs_plt: bool,
+ pub plt_by_default: bool,
/// Either partial, full, or off. Full RELRO makes the dynamic linker
/// resolve all symbols at startup and marks the GOT read-only before
/// starting the program, preventing overwriting the GOT.
@@ -1798,7 +2023,7 @@ impl TargetOptions {
}
fn update_from_cli(&mut self) {
- self.linker_flavor = LinkerFlavor::from_cli_impl(
+ self.linker_flavor = LinkerFlavor::from_cli_json(
self.linker_flavor_json,
self.lld_flavor_json,
self.linker_is_gnu_json,
@@ -1812,12 +2037,7 @@ impl TargetOptions {
] {
args.clear();
for (flavor, args_json) in args_json {
- // Cannot use `from_cli` due to borrow checker.
- let linker_flavor = LinkerFlavor::from_cli_impl(
- *flavor,
- self.lld_flavor_json,
- self.linker_is_gnu_json,
- );
+ let linker_flavor = self.linker_flavor.with_cli_hints(*flavor);
// Normalize to no lld to avoid asserts.
let linker_flavor = match linker_flavor {
LinkerFlavor::Gnu(cc, _) => LinkerFlavor::Gnu(cc, Lld::No),
@@ -1900,7 +2120,7 @@ impl Default for TargetOptions {
no_default_libraries: true,
position_independent_executables: false,
static_position_independent_executables: false,
- needs_plt: false,
+ plt_by_default: true,
relro_level: RelroLevel::None,
pre_link_objects: Default::default(),
post_link_objects: Default::default(),
@@ -2364,6 +2584,7 @@ impl Target {
Some("leak") => SanitizerSet::LEAK,
Some("memory") => SanitizerSet::MEMORY,
Some("memtag") => SanitizerSet::MEMTAG,
+ Some("safestack") => SanitizerSet::SAFESTACK,
Some("shadow-call-stack") => SanitizerSet::SHADOWCALLSTACK,
Some("thread") => SanitizerSet::THREAD,
Some("hwaddress") => SanitizerSet::HWADDRESS,
@@ -2572,7 +2793,7 @@ impl Target {
key!(no_default_libraries, bool);
key!(position_independent_executables, bool);
key!(static_position_independent_executables, bool);
- key!(needs_plt, bool);
+ key!(plt_by_default, bool);
key!(relro_level, RelroLevel)?;
key!(archive_format);
key!(allow_asm, bool);
@@ -2828,7 +3049,7 @@ impl ToJson for Target {
target_option_val!(no_default_libraries);
target_option_val!(position_independent_executables);
target_option_val!(static_position_independent_executables);
- target_option_val!(needs_plt);
+ target_option_val!(plt_by_default);
target_option_val!(relro_level);
target_option_val!(archive_format);
target_option_val!(allow_asm);
diff --git a/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs
new file mode 100644
index 000000000..079506540
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv32imac_esp_espidf.rs
@@ -0,0 +1,31 @@
+use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+ llvm_target: "riscv32".into(),
+ pointer_width: 32,
+ arch: "riscv32".into(),
+
+ options: TargetOptions {
+ families: cvs!["unix"],
+ os: "espidf".into(),
+ env: "newlib".into(),
+ vendor: "espressif".into(),
+ linker: Some("riscv32-esp-elf-gcc".into()),
+ cpu: "generic-rv32".into(),
+
+ // As RiscV32IMAC architecture does natively support atomics,
+ // automatically enable the support for the Rust STD library.
+ max_atomic_width: Some(64),
+ atomic_cas: true,
+
+ features: "+m,+a,+c".into(),
+ panic_strategy: PanicStrategy::Abort,
+ relocation_model: RelocModel::Static,
+ emit_debug_gdb_scripts: false,
+ eh_frame_header: false,
+ ..Default::default()
+ },
+ }
+}
diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs
new file mode 100644
index 000000000..a89bd363a
--- /dev/null
+++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_netbsd.rs
@@ -0,0 +1,19 @@
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-netbsd".into(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
+ arch: "riscv64".into(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".into(),
+ features: "+m,+a,+f,+d,+c".into(),
+ llvm_abiname: "lp64d".into(),
+ max_atomic_width: Some(64),
+ mcount: "__mcount".into(),
+ ..super::netbsd_base::opts()
+ },
+ }
+}
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 1dcb47056..061b6a96f 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs
@@ -13,7 +13,7 @@ pub fn target() -> Target {
.into(),
arch: arch.target_arch(),
options: TargetOptions {
- max_atomic_width: Some(64),
+ max_atomic_width: Some(128),
stack_probes: StackProbeType::X86,
..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 9f3b0fab6..50f359c35 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
@@ -15,7 +15,7 @@ pub fn target() -> Target {
.into(),
arch: arch.target_arch(),
options: TargetOptions {
- max_atomic_width: Some(64),
+ max_atomic_width: Some(128),
stack_probes: StackProbeType::X86,
..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 550ce0b9c..2ec4d9569 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs
@@ -1,15 +1,16 @@
-use super::apple_base::{opts, Arch};
+use super::apple_base::{opts, tvos_sim_llvm_target, Arch};
use crate::spec::{StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let arch = Arch::X86_64_sim;
Target {
- llvm_target: "x86_64-apple-tvos".into(),
+ llvm_target: tvos_sim_llvm_target(arch).into(),
pointer_width: 64,
- data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".into(),
+ data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+ .into(),
arch: arch.target_arch(),
options: TargetOptions {
- max_atomic_width: Some(64),
+ max_atomic_width: Some(128),
stack_probes: StackProbeType::X86,
..opts("tvos", arch)
},
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs
index 75ce02cba..5fcc00a86 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_watchos_sim.rs
@@ -10,7 +10,7 @@ pub fn target() -> Target {
.into(),
arch: arch.target_arch(),
options: TargetOptions {
- max_atomic_width: Some(64),
+ max_atomic_width: Some(128),
stack_probes: StackProbeType::X86,
forces_embed_bitcode: true,
// Taken from a clang build on Xcode 11.4.1.
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 cba6fda19..a7ed74f47 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
@@ -63,6 +63,7 @@ pub fn target() -> Target {
linker: Some("rust-lld".into()),
max_atomic_width: Some(64),
cpu: "x86-64".into(),
+ plt_by_default: false,
features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(),
llvm_args: cvs!["--x86-experimental-lvi-inline-asm-hardening"],
position_independent_executables: true,
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 a3bdb5f54..c110674fd 100644
--- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, T
pub fn target() -> Target {
let mut base = super::android_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
// https://developer.android.com/ndk/guides/abis.html#86-64
base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".into();
base.max_atomic_width = Some(64);
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
index 6fb2dfd80..8424757df 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_nto_qnx710.rs
@@ -10,6 +10,7 @@ pub fn target() -> Target {
arch: "x86_64".into(),
options: TargetOptions {
cpu: "x86-64".into(),
+ plt_by_default: false,
max_atomic_width: Some(64),
pre_link_args: TargetOptions::link_args(
LinkerFlavor::Gnu(Cc::Yes, Lld::No),
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
index cb62a8173..e2c59d293 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
@@ -4,10 +4,11 @@ pub fn target() -> Target {
let mut base = super::solaris_base::opts();
base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64"]);
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.vendor = "pc".into();
base.max_atomic_width = Some(64);
base.stack_probes = StackProbeType::X86;
- base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;
Target {
llvm_target: "x86_64-pc-solaris".into(),
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 37feaa9db..1b8885c34 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
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, Target};
pub fn target() -> Target {
let mut base = super::windows_gnu_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
// Use high-entropy 64 bit address space for ASLR
base.add_pre_link_args(
LinkerFlavor::Gnu(Cc::No, Lld::No),
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
index 039bc2bd2..8f5e398a0 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, Target};
pub fn target() -> Target {
let mut base = super::windows_gnullvm_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.max_atomic_width = Some(64);
base.linker = Some("x86_64-w64-mingw32-clang".into());
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 081806aa6..6b897ca70 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
@@ -3,6 +3,7 @@ use crate::spec::Target;
pub fn target() -> Target {
let mut base = super::windows_msvc_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
Target {
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 0f31ea86b..650065f63 100644
--- a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
+++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
@@ -4,6 +4,7 @@ pub fn target() -> Target {
let mut base = super::solaris_base::opts();
base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64"]);
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.vendor = "sun".into();
base.max_atomic_width = Some(64);
base.stack_probes = StackProbeType::X86;
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 67ce3768d..3b8e75977 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::dragonfly_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
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 b41e5842a..b2d91d099 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::freebsd_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs
index a3231d19f..bee935419 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs
@@ -3,6 +3,7 @@ use crate::spec::{SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::fuchsia_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.stack_probes = StackProbeType::X86;
base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
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 9a7a3b501..16ed3150e 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::haiku_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
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 fb1af33f8..74ef2527c 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs
@@ -3,6 +3,7 @@ use crate::spec::{StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::hermit_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.features = "+rdrnd,+rdseed".into();
base.stack_probes = StackProbeType::X86;
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 04a12a7bf..9259cfe5f 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
@@ -4,8 +4,9 @@ pub fn target() -> Target {
let mut base = super::illumos_base::opts();
base.add_pre_link_args(LinkerFlavor::Unix(Cc::Yes), &["-m64", "-std=c99"]);
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
- base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+ base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;
Target {
// LLVM does not currently have a separate illumos target,
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 26da7e800..912d289c4 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
@@ -3,6 +3,7 @@ use crate::spec::{PanicStrategy, Target};
pub fn target() -> Target {
let mut base = super::l4re_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.panic_strategy = PanicStrategy::Abort;
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 9af1049b8..2f970f87c 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
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_gnu_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
@@ -11,6 +12,7 @@ pub fn target() -> Target {
| SanitizerSet::CFI
| SanitizerSet::LEAK
| SanitizerSet::MEMORY
+ | SanitizerSet::SAFESTACK
| SanitizerSet::THREAD;
base.supports_xray = true;
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 626d5b480..5469d02c5 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
@@ -10,7 +10,7 @@ pub fn target() -> Target {
base.has_thread_local = false;
// BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI
// breaks code gen. See LLVM bug 36743
- base.needs_plt = true;
+ base.plt_by_default = true;
Target {
llvm_target: "x86_64-unknown-linux-gnux32".into(),
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 bf4cf7d7b..7154f5fa3 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
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
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 74c434935..2e7bf34f7 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, StackProbeType, Target, T
pub fn target() -> Target {
let mut base = super::netbsd_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
index 43c5ce78c..fe3b24f2d 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none.rs
@@ -10,6 +10,7 @@ use super::{RelroLevel, SanitizerSet, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
let opts = TargetOptions {
cpu: "x86-64".into(),
+ plt_by_default: false,
max_atomic_width: Some(64),
stack_probes: StackProbeType::X86,
position_independent_executables: true,
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 8e4d42a0a..86fa9bf7e 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::openbsd_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
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 b47f15cf5..decc97367 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::redox_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
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 a7ae17839..67664a747 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs
@@ -10,6 +10,7 @@ use crate::spec::Target;
pub fn target() -> Target {
let mut base = super::uefi_msvc_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
// We disable MMX and SSE for now, even though UEFI allows using them. Problem is, you have to
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 c3eaa6939..1a9d2a571 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
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, Target};
pub fn target() -> Target {
let mut base = super::windows_uwp_gnu_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
// Use high-entropy 64 bit address space for ASLR
base.add_pre_link_args(
LinkerFlavor::Gnu(Cc::No, Lld::No),
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 b2769350b..1ae403fa8 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
@@ -3,6 +3,7 @@ use crate::spec::Target;
pub fn target() -> Target {
let mut base = super::windows_uwp_msvc_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
Target {
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 365ade6bc..a7c4aaecf 100644
--- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
@@ -3,6 +3,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::vxworks_base::opts();
base.cpu = "x86-64".into();
+ base.plt_by_default = false;
base.max_atomic_width = Some(64);
base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]);
base.stack_probes = StackProbeType::X86;
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 312bd3817..6efc1e730 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -1,5 +1,5 @@
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::{self, ObligationCtxt};
+use crate::traits::{self, DefiningAnchor, ObligationCtxt};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
@@ -80,7 +80,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
pub trait InferCtxtBuilderExt<'tcx> {
fn enter_canonical_trait_query<K, R>(
- &mut self,
+ self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
@@ -108,7 +108,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
/// have `'tcx` be free on this function so that we can talk about
/// `K: TypeFoldable<TyCtxt<'tcx>>`.)
fn enter_canonical_trait_query<K, R>(
- &mut self,
+ self,
canonical_key: &Canonical<'tcx, K>,
operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>,
) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution>
@@ -117,8 +117,9 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
{
- let (infcx, key, canonical_inference_vars) =
- self.build_with_canonical(DUMMY_SP, canonical_key);
+ let (infcx, key, canonical_inference_vars) = self
+ .with_opaque_type_inference(DefiningAnchor::Bubble)
+ .build_with_canonical(DUMMY_SP, canonical_key);
let ocx = ObligationCtxt::new(&infcx);
let value = operation(&ocx, key)?;
ocx.make_canonicalized_query_response(canonical_inference_vars, value)
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index ed3994be9..56d37d58d 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -14,8 +14,7 @@
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
-#![feature(drain_filter)]
-#![feature(hash_drain_filter)]
+#![feature(extract_if)]
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(never_type)]
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
new file mode 100644
index 000000000..422a6ee34
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -0,0 +1,195 @@
+use super::{EvalCtxt, SolverMode};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+/// We may need to invert the alias relation direction if dealing an alias on the RHS.
+#[derive(Debug)]
+enum Invert {
+ No,
+ Yes,
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn compute_alias_relate_goal(
+ &mut self,
+ goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
+ if lhs.is_infer() || rhs.is_infer() {
+ bug!(
+ "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
+ );
+ }
+
+ match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
+ (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
+
+ // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
+ (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_lhs,
+ rhs,
+ direction,
+ Invert::No,
+ ),
+
+ // LHS is not a projection, only way this is true is if RHS normalizes-to LHS
+ (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_rhs,
+ lhs,
+ direction,
+ Invert::Yes,
+ ),
+
+ (Some(alias_lhs), Some(alias_rhs)) => {
+ debug!("both sides are aliases");
+
+ let mut candidates = Vec::new();
+ // LHS normalizes-to RHS
+ candidates.extend(self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_lhs,
+ rhs,
+ direction,
+ Invert::No,
+ ));
+ // RHS normalizes-to RHS
+ candidates.extend(self.assemble_normalizes_to_candidate(
+ param_env,
+ alias_rhs,
+ lhs,
+ direction,
+ Invert::Yes,
+ ));
+ // Relate via substs
+ let subst_relate_response = self
+ .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
+ candidates.extend(subst_relate_response);
+ debug!(?candidates);
+
+ if let Some(merged) = self.try_merge_responses(&candidates) {
+ Ok(merged)
+ } else {
+ // When relating two aliases and we have ambiguity, we prefer
+ // relating the generic arguments of the aliases over normalizing
+ // them. This is necessary for inference during typeck.
+ //
+ // As this is incomplete, we must not do so during coherence.
+ match self.solver_mode() {
+ SolverMode::Normal => {
+ if let Ok(subst_relate_response) = subst_relate_response {
+ Ok(subst_relate_response)
+ } else if let Ok(bidirectional_normalizes_to_response) = self
+ .assemble_bidirectional_normalizes_to_candidate(
+ param_env, lhs, rhs, direction,
+ )
+ {
+ Ok(bidirectional_normalizes_to_response)
+ } else {
+ self.flounder(&candidates)
+ }
+ }
+ SolverMode::Coherence => self.flounder(&candidates),
+ }
+ }
+ }
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn assemble_normalizes_to_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias: ty::AliasTy<'tcx>,
+ other: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ invert: Invert,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("normalizes-to").enter(|ecx| {
+ ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn normalizes_to_inner(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias: ty::AliasTy<'tcx>,
+ other: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ invert: Invert,
+ ) -> Result<(), NoSolution> {
+ let other = match direction {
+ // This is purely an optimization.
+ ty::AliasRelationDirection::Equate => other,
+
+ ty::AliasRelationDirection::Subtype => {
+ let fresh = self.next_term_infer_of_kind(other);
+ let (sub, sup) = match invert {
+ Invert::No => (fresh, other),
+ Invert::Yes => (other, fresh),
+ };
+ self.sub(param_env, sub, sup)?;
+ fresh
+ }
+ };
+ self.add_goal(Goal::new(
+ self.tcx(),
+ param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
+ ));
+
+ Ok(())
+ }
+
+ fn assemble_subst_relate_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ alias_lhs: ty::AliasTy<'tcx>,
+ alias_rhs: ty::AliasTy<'tcx>,
+ direction: ty::AliasRelationDirection,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("substs relate").enter(|ecx| {
+ match direction {
+ ty::AliasRelationDirection::Equate => {
+ ecx.eq(param_env, alias_lhs, alias_rhs)?;
+ }
+ ty::AliasRelationDirection::Subtype => {
+ ecx.sub(param_env, alias_lhs, alias_rhs)?;
+ }
+ }
+
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+
+ fn assemble_bidirectional_normalizes_to_candidate(
+ &mut self,
+ param_env: ty::ParamEnv<'tcx>,
+ lhs: ty::Term<'tcx>,
+ rhs: ty::Term<'tcx>,
+ direction: ty::AliasRelationDirection,
+ ) -> QueryResult<'tcx> {
+ self.probe_candidate("bidir normalizes-to").enter(|ecx| {
+ ecx.normalizes_to_inner(
+ param_env,
+ lhs.to_alias_ty(ecx.tcx()).unwrap(),
+ rhs,
+ direction,
+ Invert::No,
+ )?;
+ ecx.normalizes_to_inner(
+ param_env,
+ rhs.to_alias_ty(ecx.tcx()).unwrap(),
+ lhs,
+ direction,
+ Invert::Yes,
+ )?;
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ })
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index f32ff0442..28138054a 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::elaborate;
use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult};
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::TypeFoldable;
@@ -48,7 +49,7 @@ pub(super) enum CandidateSource {
/// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
/// For a list of all traits with builtin impls, check out the
/// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
- BuiltinImpl,
+ BuiltinImpl(BuiltinImplSource),
/// An assumption from the environment.
///
/// More precisely we've used the `n-th` assumption in the `param_env`.
@@ -86,6 +87,16 @@ pub(super) enum CandidateSource {
AliasBound,
}
+/// Records additional information about what kind of built-in impl this is.
+/// This should only be used by selection.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum BuiltinImplSource {
+ TraitUpcasting,
+ Object,
+ Misc,
+ Ambiguity,
+}
+
/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
@@ -105,7 +116,7 @@ pub(super) trait GoalKind<'tcx>:
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx>;
@@ -115,7 +126,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_implied_clause(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
@@ -131,7 +142,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_alias_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
ecx.validate_alias_bound_self_from_param_env(goal)
@@ -144,7 +155,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_object_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
let tcx = ecx.tcx();
@@ -294,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// least structurally resolve the type one layer.
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
- source: CandidateSource::BuiltinImpl,
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
@@ -320,11 +331,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates
}
- /// If the self type of a goal is a projection, computing the relevant candidates is difficult.
+ /// If the self type of a goal is an alias we first try to normalize the self type
+ /// and compute the candidates for the normalized self type in case that succeeds.
+ ///
+ /// These candidates are used in addition to the ones with the alias as a self type.
+ /// We do this to simplify both builtin candidates and for better performance.
+ ///
+ /// We generate the builtin candidates on the fly by looking at the self type, e.g.
+ /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin
+ /// candidates while the self type is still an alias seems difficult. This is similar
+ /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented).
///
- /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
- /// self type to the list of candidates in case that succeeds. We also have to consider candidates with the
- /// projection as a self type as well
+ /// Looking at all impls for some trait goal is prohibitively expensive. We therefore
+ /// only look at implementations with a matching self type. Because of this function,
+ /// we can avoid looking at all existing impls if the self type is an alias.
#[instrument(level = "debug", skip_all)]
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
&mut self,
@@ -336,37 +356,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
return
};
- let normalized_self_candidates: Result<_, NoSolution> = self.probe(|ecx| {
- ecx.with_incremented_depth(
- |ecx| {
- let result = ecx.evaluate_added_goals_and_make_canonical_response(
- Certainty::Maybe(MaybeCause::Overflow),
- )?;
- Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
- },
- |ecx| {
- let normalized_ty = ecx.next_ty_infer();
- let normalizes_to_goal = goal.with(
- tcx,
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty,
- term: normalized_ty.into(),
- }),
- );
- ecx.add_goal(normalizes_to_goal);
- let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
- debug!("self type normalization failed");
- })?;
- let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
- debug!(?normalized_ty, "self type normalized");
- // NOTE: Alternatively we could call `evaluate_goal` here and only
- // have a `Normalized` candidate. This doesn't work as long as we
- // use `CandidateSource` in winnowing.
- let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
- Ok(ecx.assemble_and_evaluate_candidates(goal))
- },
- )
- });
+ let normalized_self_candidates: Result<_, NoSolution> =
+ self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
+ ecx.with_incremented_depth(
+ |ecx| {
+ let result = ecx.evaluate_added_goals_and_make_canonical_response(
+ Certainty::Maybe(MaybeCause::Overflow),
+ )?;
+ Ok(vec![Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+ result,
+ }])
+ },
+ |ecx| {
+ let normalized_ty = ecx.next_ty_infer();
+ let normalizes_to_goal = goal.with(
+ tcx,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty,
+ term: normalized_ty.into(),
+ }),
+ );
+ ecx.add_goal(normalizes_to_goal);
+ let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
+ debug!("self type normalization failed");
+ })?;
+ let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
+ debug!(?normalized_ty, "self type normalized");
+ // NOTE: Alternatively we could call `evaluate_goal` here and only
+ // have a `Normalized` candidate. This doesn't work as long as we
+ // use `CandidateSource` in winnowing.
+ let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+ Ok(ecx.assemble_and_evaluate_candidates(goal))
+ },
+ )
+ });
if let Ok(normalized_self_candidates) = normalized_self_candidates {
candidates.extend(normalized_self_candidates);
@@ -445,9 +469,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};
match result {
- Ok(result) => {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
Err(NoSolution) => (),
}
@@ -455,7 +480,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) {
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result });
+ candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
+ result,
+ });
}
}
}
@@ -508,10 +536,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
| ty::Placeholder(..)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Alias(ty::Inherent, _)
+ | ty::Alias(ty::Weak, _)
| ty::Error(_) => return,
ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
| ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty,
};
@@ -618,9 +647,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};
match result {
- Ok(result) => {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
+ result,
+ }),
Err(NoSolution) => (),
}
}
@@ -631,6 +661,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
+ let tcx = self.tcx();
+ if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
+ return;
+ }
+
let self_ty = goal.predicate.self_ty();
let bounds = match *self_ty.kind() {
ty::Bool
@@ -663,7 +698,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(bounds, ..) => bounds,
};
- let tcx = self.tcx();
let own_bounds: FxIndexSet<_> =
bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect();
for assumption in elaborate(tcx, own_bounds.iter().copied())
@@ -675,17 +709,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// projection predicates that we reach by elaborating the principal trait ref,
// since that'll cause ambiguity.
//
- // We can remove this when we have implemented intersections in responses.
- if assumption.to_opt_poly_projection_pred().is_some()
- && !own_bounds.contains(&assumption)
- {
+ // We can remove this when we have implemented lifetime intersections in responses.
+ if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) {
continue;
}
match G::consider_object_bound_candidate(self, goal, assumption) {
- Ok(result) => {
- candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
- }
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ result,
+ }),
Err(NoSolution) => (),
}
}
@@ -706,8 +739,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(_) => match self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
{
- Ok(result) => candidates
- .push(Candidate { source: CandidateSource::BuiltinImpl, result }),
+ Ok(result) => candidates.push(Candidate {
+ source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
+ result,
+ }),
// FIXME: This will be reachable at some point if we're in
// `assemble_candidates_after_normalizing_self_ty` and we get a
// universe error. We'll deal with it at this point.
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 0ede32c75..3bb8cad15 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -28,12 +28,12 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
| ty::Char => Ok(vec![]),
// Treat `str` like it's defined as `struct str([u8]);`
- ty::Str => Ok(vec![tcx.mk_slice(tcx.types.u8)]),
+ ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]),
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
| ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(_) => {
@@ -96,7 +96,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) };
counter += 1;
- tcx.mk_re_late_bound(current_depth, br)
+ ty::Region::new_late_bound(tcx, current_depth, br)
}
// All free regions should be erased here.
r => bug!("unexpected region: {r:?}"),
@@ -148,11 +148,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
ty::Adt(def, substs) => {
let sized_crit = def.sized_constraint(ecx.tcx());
- Ok(sized_crit
- .0
- .iter()
- .map(|ty| sized_crit.rebind(*ty).subst(ecx.tcx(), substs))
- .collect())
+ Ok(sized_crit.subst_iter_copied(ecx.tcx(), substs).collect())
}
}
}
@@ -237,7 +233,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
{
Ok(Some(
sig.subst(tcx, substs)
- .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())),
+ .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
))
} else {
Err(NoSolution)
@@ -246,7 +242,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
ty::FnPtr(sig) => {
if sig.is_fn_trait_compatible() {
- Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output()))))
+ Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output()))))
} else {
Err(NoSolution)
}
@@ -347,7 +343,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-) -> Vec<ty::Predicate<'tcx>> {
+) -> Vec<ty::Clause<'tcx>> {
let tcx = ecx.tcx();
let mut requirements = vec![];
requirements.extend(
@@ -357,7 +353,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
// FIXME(associated_const_equality): Also add associated consts to
// the requirements here.
if item.kind == ty::AssocKind::Type {
- requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs));
+ requirements.extend(tcx.item_bounds(item.def_id).subst_iter(tcx, trait_ref.substs));
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
index ff4bff10c..255620489 100644
--- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs
@@ -208,8 +208,25 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
t
}
- fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- let r = self.infcx.shallow_resolve(r);
+ fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ match self.canonicalize_mode {
+ CanonicalizeMode::Input => {
+ // Don't resolve infer vars in input, since it affects
+ // caching and may cause trait selection bugs which rely
+ // on regions to be equal.
+ }
+ CanonicalizeMode::Response { .. } => {
+ if let ty::ReVar(vid) = *r {
+ r = self
+ .infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(self.infcx.tcx, vid);
+ }
+ }
+ }
+
let kind = match *r {
ty::ReLateBound(..) => return r,
@@ -255,7 +272,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
}),
);
let br = ty::BoundRegion { var, kind: BrAnon(None) };
- self.interner().mk_re_late_bound(self.binder_index, br)
+ ty::Region::new_late_bound(self.interner(), self.binder_index, br)
}
fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
@@ -266,7 +283,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
// any equated inference vars correctly!
let root_vid = self.infcx.root_var(vid);
if root_vid != vid {
- t = self.infcx.tcx.mk_ty_var(root_vid);
+ t = Ty::new_var(self.infcx.tcx, root_vid);
vid = root_vid;
}
@@ -349,7 +366,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
}),
);
let bt = ty::BoundTy { var, kind: BoundTyKind::Anon };
- self.interner().mk_bound(self.binder_index, bt)
+ Ty::new_bound(self.infcx.tcx, self.binder_index, bt)
}
fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> {
@@ -360,7 +377,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
// any equated inference vars correctly!
let root_vid = self.infcx.root_const_var(vid);
if root_vid != vid {
- c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty());
+ c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty());
vid = root_vid;
}
@@ -409,6 +426,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> {
var
}),
);
- self.interner().mk_const(ty::ConstKind::Bound(self.binder_index, var), c.ty())
+ ty::Const::new_bound(self.infcx.tcx, self.binder_index, var, c.ty())
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index f91c67277..74dfbdddb 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -9,25 +9,32 @@ use rustc_infer::infer::{
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::solve::inspect;
use rustc_middle::traits::solve::{
- CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
- PredefinedOpaquesData, QueryResult,
+ CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause,
+ PredefinedOpaques, PredefinedOpaquesData, QueryResult,
};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{
- self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
- TypeVisitor,
+ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor,
};
+use rustc_session::config::DumpSolverProofTree;
use rustc_span::DUMMY_SP;
+use std::io::Write;
use std::ops::ControlFlow;
use crate::traits::specialization_graph;
+use super::inspect::ProofTreeBuilder;
use super::search_graph::{self, OverflowHandler};
use super::SolverMode;
use super::{search_graph::SearchGraph, Goal};
+pub use select::InferCtxtSelectExt;
mod canonical;
+mod probe;
+mod select;
pub struct EvalCtxt<'a, 'tcx> {
/// The inference context that backs (mostly) inference and placeholder terms
@@ -73,12 +80,8 @@ pub struct EvalCtxt<'a, 'tcx> {
// ambiguous goals. Instead, a probe needs to be introduced somewhere in the
// evaluation code.
tainted: Result<(), NoSolution>,
-}
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub(super) enum IsNormalizesToHack {
- Yes,
- No,
+ inspect: ProofTreeBuilder<'tcx>,
}
#[derive(Debug, Clone)]
@@ -110,6 +113,26 @@ impl NestedGoals<'_> {
}
}
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum GenerateProofTree {
+ Yes(UseGlobalCache),
+ No,
+}
+
+#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+pub enum UseGlobalCache {
+ Yes,
+ No,
+}
+impl UseGlobalCache {
+ pub fn from_bool(use_cache: bool) -> Self {
+ match use_cache {
+ true => UseGlobalCache::Yes,
+ false => UseGlobalCache::No,
+ }
+ }
+}
+
pub trait InferCtxtEvalExt<'tcx> {
/// Evaluates a goal from **outside** of the trait solver.
///
@@ -118,7 +141,11 @@ pub trait InferCtxtEvalExt<'tcx> {
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>;
+ generate_proof_tree: GenerateProofTree,
+ ) -> (
+ Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+ Option<inspect::GoalEvaluation<'tcx>>,
+ );
}
impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
@@ -126,16 +153,39 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
fn evaluate_root_goal(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
- let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
- let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
+ generate_proof_tree: GenerateProofTree,
+ ) -> (
+ Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
+ Option<inspect::GoalEvaluation<'tcx>>,
+ ) {
+ EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+ ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+ })
+ }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ pub(super) fn solver_mode(&self) -> SolverMode {
+ self.search_graph.solver_mode()
+ }
+
+ /// Creates a root evaluation context and search graph. This should only be
+ /// used from outside of any evaluation, and other methods should be preferred
+ /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+ fn enter_root<R>(
+ infcx: &InferCtxt<'tcx>,
+ generate_proof_tree: GenerateProofTree,
+ f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
+ ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
+ let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
+ let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
- infcx: self,
+ infcx: infcx,
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
- predefined_opaques_in_body: self
+ predefined_opaques_in_body: infcx
.tcx
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
// Only relevant when canonicalizing the response.
@@ -143,8 +193,18 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
tainted: Ok(()),
+ inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
};
- let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
+ let result = f(&mut ecx);
+
+ let tree = ecx.inspect.finalize();
+ if let (Some(tree), DumpSolverProofTree::Always) =
+ (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree)
+ {
+ let mut lock = std::io::stdout().lock();
+ let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.flush();
+ }
assert!(
ecx.nested_goals.is_empty(),
@@ -152,13 +212,68 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
);
assert!(search_graph.is_empty());
- result
+ (result, tree)
}
-}
-impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
- pub(super) fn solver_mode(&self) -> SolverMode {
- self.search_graph.solver_mode()
+ /// Creates a nested evaluation context that shares the same search graph as the
+ /// one passed in. This is suitable for evaluation, granted that the search graph
+ /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
+ /// but it's preferable to use other methods that call this one rather than this
+ /// method directly.
+ ///
+ /// This function takes care of setting up the inference context, setting the anchor,
+ /// and registering opaques from the canonicalized input.
+ fn enter_canonical<R>(
+ tcx: TyCtxt<'tcx>,
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+ canonical_input: CanonicalInput<'tcx>,
+ goal_evaluation: &mut ProofTreeBuilder<'tcx>,
+ f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+ ) -> R {
+ let intercrate = match search_graph.solver_mode() {
+ SolverMode::Normal => false,
+ SolverMode::Coherence => true,
+ };
+ let (ref infcx, input, var_values) = tcx
+ .infer_ctxt()
+ .intercrate(intercrate)
+ .with_next_trait_solver(true)
+ .with_opaque_type_inference(canonical_input.value.anchor)
+ .build_with_canonical(DUMMY_SP, &canonical_input);
+
+ let mut ecx = EvalCtxt {
+ infcx,
+ var_values,
+ predefined_opaques_in_body: input.predefined_opaques_in_body,
+ max_input_universe: canonical_input.max_universe,
+ search_graph,
+ nested_goals: NestedGoals::new(),
+ tainted: Ok(()),
+ inspect: goal_evaluation.new_goal_evaluation_step(input),
+ };
+
+ for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
+ ecx.insert_hidden_type(key, input.goal.param_env, ty)
+ .expect("failed to prepopulate opaque types");
+ }
+
+ if !ecx.nested_goals.is_empty() {
+ panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
+ }
+
+ let result = f(&mut ecx, input.goal);
+
+ goal_evaluation.goal_evaluation_step(ecx.inspect);
+
+ // When creating a query response we clone the opaque type constraints
+ // instead of taking them. This would cause an ICE here, since we have
+ // assertions against dropping an `InferCtxt` without taking opaques.
+ // FIXME: Once we remove support for the old impl we can remove this.
+ if input.anchor != DefiningAnchor::Error {
+ let _ = infcx.take_opaque_types();
+ }
+
+ result
}
/// The entry point of the solver.
@@ -170,58 +285,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
/// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
/// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
/// outside of it.
- #[instrument(level = "debug", skip(tcx, search_graph), ret)]
+ #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_input: CanonicalInput<'tcx>,
+ mut goal_evaluation: &mut ProofTreeBuilder<'tcx>,
) -> QueryResult<'tcx> {
+ goal_evaluation.canonicalized_goal(canonical_input);
+
// Deal with overflow, caching, and coinduction.
//
// The actual solver logic happens in `ecx.compute_goal`.
- search_graph.with_new_goal(tcx, canonical_input, |search_graph| {
- let intercrate = match search_graph.solver_mode() {
- SolverMode::Normal => false,
- SolverMode::Coherence => true,
- };
- let (ref infcx, input, var_values) = tcx
- .infer_ctxt()
- .intercrate(intercrate)
- .with_opaque_type_inference(canonical_input.value.anchor)
- .build_with_canonical(DUMMY_SP, &canonical_input);
-
- for &(a, b) in &input.predefined_opaques_in_body.opaque_types {
- let InferOk { value: (), obligations } = infcx
- .register_hidden_type_in_new_solver(a, input.goal.param_env, b)
- .expect("expected opaque type instantiation to succeed");
- // We're only registering opaques already defined by the caller,
- // so we're not responsible for proving that they satisfy their
- // item bounds, unless we use them in a normalizes-to goal,
- // which is handled in `EvalCtxt::unify_existing_opaque_tys`.
- let _ = obligations;
- }
- let mut ecx = EvalCtxt {
- infcx,
- var_values,
- predefined_opaques_in_body: input.predefined_opaques_in_body,
- max_input_universe: canonical_input.max_universe,
- search_graph,
- nested_goals: NestedGoals::new(),
- tainted: Ok(()),
- };
-
- let result = ecx.compute_goal(input.goal);
-
- // When creating a query response we clone the opaque type constraints
- // instead of taking them. This would cause an ICE here, since we have
- // assertions against dropping an `InferCtxt` without taking opaques.
- // FIXME: Once we remove support for the old impl we can remove this.
- if input.anchor != DefiningAnchor::Error {
- let _ = infcx.take_opaque_types();
- }
-
- result
- })
+ search_graph.with_new_goal(
+ tcx,
+ canonical_input,
+ goal_evaluation,
+ |search_graph, goal_evaluation| {
+ EvalCtxt::enter_canonical(
+ tcx,
+ search_graph,
+ canonical_input,
+ goal_evaluation,
+ |ecx, goal| {
+ let result = ecx.compute_goal(goal);
+ ecx.inspect.query_result(result);
+ result
+ },
+ )
+ },
+ )
}
/// Recursively evaluates `goal`, returning whether any inference vars have
@@ -232,16 +325,37 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let canonical_response =
- EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
+ let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+ let canonical_response = EvalCtxt::evaluate_canonical_goal(
+ self.tcx(),
+ self.search_graph,
+ canonical_goal,
+ &mut goal_evaluation,
+ );
+ goal_evaluation.query_result(canonical_response);
+ let canonical_response = match canonical_response {
+ Err(e) => {
+ self.inspect.goal_evaluation(goal_evaluation);
+ return Err(e);
+ }
+ Ok(response) => response,
+ };
let has_changed = !canonical_response.value.var_values.is_identity()
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
- let (certainty, nested_goals) = self.instantiate_and_apply_query_response(
+ let (certainty, nested_goals) = match self.instantiate_and_apply_query_response(
goal.param_env,
orig_values,
canonical_response,
- )?;
+ ) {
+ Err(e) => {
+ self.inspect.goal_evaluation(goal_evaluation);
+ return Err(e);
+ }
+ Ok(response) => response,
+ };
+ goal_evaluation.returned_goals(&nested_goals);
+ self.inspect.goal_evaluation(goal_evaluation);
if !has_changed && !nested_goals.is_empty() {
bug!("an unchanged goal shouldn't have any side-effects on instantiation");
@@ -261,9 +375,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
{
debug!("rerunning goal to check result is stable");
let (_orig_values, canonical_goal) = self.canonicalize_goal(goal);
- let new_canonical_response =
- EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
- if !new_canonical_response.value.var_values.is_identity() {
+ let new_canonical_response = EvalCtxt::evaluate_canonical_goal(
+ self.tcx(),
+ self.search_graph,
+ canonical_goal,
+ // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal`
+ &mut ProofTreeBuilder::new_noop(),
+ )?;
+ // We only check for modulo regions as we convert all regions in
+ // the input to new existentials, even if they're expected to be
+ // `'static` or a placeholder region.
+ if !new_canonical_response.value.var_values.is_identity_modulo_regions() {
bug!(
"unstable result: re-canonicalized goal={canonical_goal:#?} \
first_response={canonical_response:#?} \
@@ -287,19 +409,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let kind = predicate.kind();
if let Some(kind) = kind.no_bound_vars() {
match kind {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
self.compute_trait_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate })
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
}
ty::PredicateKind::Subtype(predicate) => {
@@ -316,24 +438,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::ObjectSafe(trait_def_id) => {
self.compute_object_safe_goal(trait_def_id)
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
self.compute_well_formed_goal(Goal { param_env, predicate: arg })
}
- ty::PredicateKind::Ambiguous => {
- self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
- }
- // FIXME: implement these predicates :)
- ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
- self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
+ self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
+ ty::PredicateKind::ConstEquate(_, _) => {
+ bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active")
}
ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self
.compute_alias_relate_goal(Goal {
param_env,
predicate: (lhs, rhs, direction),
}),
+ ty::PredicateKind::Ambiguous => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
}
} else {
let kind = self.infcx.instantiate_binder_with_placeholders(kind);
@@ -347,12 +468,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// the certainty of all the goals.
#[instrument(level = "debug", skip(self))]
pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> {
+ let inspect = self.inspect.new_evaluate_added_goals();
+ let inspect = core::mem::replace(&mut self.inspect, inspect);
+
let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
let mut new_goals = NestedGoals::new();
let response = self.repeat_while_none(
|_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
|this| {
+ this.inspect.evaluate_added_goals_loop_start();
+
let mut has_changed = Err(Certainty::Yes);
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
@@ -441,29 +567,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
},
);
+ self.inspect.eval_added_goals_result(response);
+
if response.is_err() {
self.tainted = Err(NoSolution);
}
+ let goal_evaluations = std::mem::replace(&mut self.inspect, inspect);
+ self.inspect.added_goals_evaluation(goal_evaluations);
+
self.nested_goals = goals;
response
}
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
- pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
- let mut ecx = EvalCtxt {
- infcx: self.infcx,
- var_values: self.var_values,
- predefined_opaques_in_body: self.predefined_opaques_in_body,
- max_input_universe: self.max_input_universe,
- search_graph: self.search_graph,
- nested_goals: self.nested_goals.clone(),
- tainted: self.tainted,
- };
- self.infcx.probe(|_| f(&mut ecx))
- }
-
pub(super) fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
@@ -706,6 +824,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
scope: Ty<'tcx>,
assume: rustc_transmute::Assume,
) -> Result<Certainty, NoSolution> {
+ use rustc_transmute::Answer;
// FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
ObligationCause::dummy(),
@@ -713,30 +832,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
scope,
assume,
) {
- rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
- rustc_transmute::Answer::No(_)
- | rustc_transmute::Answer::IfTransmutable { .. }
- | rustc_transmute::Answer::IfAll(_)
- | rustc_transmute::Answer::IfAny(_) => Err(NoSolution),
+ Answer::Yes => Ok(Certainty::Yes),
+ Answer::No(_) | Answer::If(_) => Err(NoSolution),
}
}
- pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool {
+ pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool {
self.infcx.opaque_type_origin(def_id).is_some()
}
- pub(super) fn register_opaque_ty(
+ pub(super) fn insert_hidden_type(
&mut self,
- a: ty::OpaqueTypeKey<'tcx>,
- b: Ty<'tcx>,
+ opaque_type_key: OpaqueTypeKey<'tcx>,
param_env: ty::ParamEnv<'tcx>,
+ hidden_ty: Ty<'tcx>,
) -> Result<(), NoSolution> {
- let InferOk { value: (), obligations } =
- self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
- self.add_goals(obligations.into_iter().map(|obligation| obligation.into()));
+ let mut obligations = Vec::new();
+ self.infcx.insert_hidden_type(
+ opaque_type_key,
+ &ObligationCause::dummy(),
+ param_env,
+ hidden_ty,
+ true,
+ &mut obligations,
+ )?;
+ self.add_goals(obligations.into_iter().map(|o| o.into()));
Ok(())
}
+ pub(super) fn add_item_bounds_for_hidden_type(
+ &mut self,
+ opaque_def_id: DefId,
+ opaque_substs: ty::SubstsRef<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ hidden_ty: Ty<'tcx>,
+ ) {
+ let mut obligations = Vec::new();
+ self.infcx.add_item_bounds_for_hidden_type(
+ opaque_def_id,
+ opaque_substs,
+ ObligationCause::dummy(),
+ param_env,
+ hidden_ty,
+ &mut obligations,
+ );
+ self.add_goals(obligations.into_iter().map(|o| o.into()));
+ }
+
// Do something for each opaque/hidden pair defined with `def_id` in the
// current inference context.
pub(super) fn unify_existing_opaque_tys(
@@ -753,23 +895,37 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if candidate_key.def_id != key.def_id {
continue;
}
- values.extend(self.probe(|ecx| {
+ values.extend(self.probe_candidate("opaque type storage").enter(|ecx| {
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
ecx.eq(param_env, a, b)?;
}
ecx.eq(param_env, candidate_ty, ty)?;
- let mut obl = vec![];
- ecx.infcx.add_item_bounds_for_hidden_type(
- candidate_key,
- ObligationCause::dummy(),
+ ecx.add_item_bounds_for_hidden_type(
+ candidate_key.def_id.to_def_id(),
+ candidate_key.substs,
param_env,
candidate_ty,
- &mut obl,
);
- ecx.add_goals(obl.into_iter().map(Into::into));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}));
}
values
}
+
+ // Try to evaluate a const, or return `None` if the const is too generic.
+ // This doesn't mean the const isn't evaluatable, though, and should be treated
+ // as an ambiguity rather than no-solution.
+ pub(super) fn try_const_eval_resolve(
+ &self,
+ param_env: ty::ParamEnv<'tcx>,
+ unevaluated: ty::UnevaluatedConst<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> Option<ty::Const<'tcx>> {
+ use rustc_middle::mir::interpret::ErrorHandled;
+ match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
+ Ok(ct) => Some(ct),
+ Err(ErrorHandled::Reported(e)) => Some(ty::Const::new_error(self.tcx(), e.into(), ty)),
+ Err(ErrorHandled::TooGeneric) => None,
+ }
+ }
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index fdb209fbf..637d45888 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -11,16 +11,16 @@
use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
use crate::solve::{CanonicalResponse, QueryResult, Response};
+use rustc_data_structures::fx::FxHashSet;
use rustc_index::IndexVec;
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
-use rustc_infer::infer::InferOk;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
};
-use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
+use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
@@ -28,10 +28,10 @@ use std::ops::Deref;
impl<'tcx> EvalCtxt<'_, 'tcx> {
/// Canonicalizes the goal remembering the original values
/// for each bound variable.
- pub(super) fn canonicalize_goal(
+ pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
- goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
+ goal: Goal<'tcx, T>,
+ ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize(
self.infcx,
@@ -137,10 +137,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
+ // We only check for leaks from universes which were entered inside
+ // of the query.
+ self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
+ debug!(?e, "failed the leak check");
+ NoSolution
+ })?;
+
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
- let region_constraints = self.infcx.with_region_constraints(|region_constraints| {
+ let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| {
make_query_region_constraints(
self.tcx(),
region_obligations
@@ -150,6 +157,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
)
});
+ let mut seen = FxHashSet::default();
+ region_constraints.outlives.retain(|outlives| seen.insert(*outlives));
+
let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
// Only return opaque type keys for newly-defined opaques
opaque_types.retain(|(a, _)| {
@@ -310,12 +320,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) -> Result<(), NoSolution> {
- for &(a, b) in opaque_types {
- let InferOk { value: (), obligations } =
- self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
- // It's sound to drop these obligations, since the normalizes-to goal
- // is responsible for proving these obligations.
- let _ = obligations;
+ for &(key, ty) in opaque_types {
+ self.insert_hidden_type(key, param_env, ty)?;
}
Ok(())
}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
new file mode 100644
index 000000000..4477ea7d5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -0,0 +1,67 @@
+use super::EvalCtxt;
+use rustc_middle::traits::solve::{inspect, QueryResult};
+use std::marker::PhantomData;
+
+pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
+ ecx: &'me mut EvalCtxt<'a, 'tcx>,
+ probe_kind: F,
+ _result: PhantomData<T>,
+}
+
+impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T>
+where
+ F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+{
+ pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T {
+ let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
+
+ let mut nested_ecx = EvalCtxt {
+ infcx: outer_ecx.infcx,
+ var_values: outer_ecx.var_values,
+ predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
+ max_input_universe: outer_ecx.max_input_universe,
+ search_graph: outer_ecx.search_graph,
+ nested_goals: outer_ecx.nested_goals.clone(),
+ tainted: outer_ecx.tainted,
+ inspect: outer_ecx.inspect.new_goal_candidate(),
+ };
+ let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
+ if !outer_ecx.inspect.is_noop() {
+ let cand_kind = probe_kind(&r);
+ nested_ecx.inspect.candidate_kind(cand_kind);
+ outer_ecx.inspect.goal_candidate(nested_ecx.inspect);
+ }
+ r
+ }
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ /// `probe_kind` is only called when proof tree building is enabled so it can be
+ /// as expensive as necessary to output the desired information.
+ pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T>
+ where
+ F: FnOnce(&T) -> inspect::CandidateKind<'tcx>,
+ {
+ ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
+ }
+
+ pub(in crate::solve) fn probe_candidate(
+ &mut self,
+ name: &'static str,
+ ) -> ProbeCtxt<
+ '_,
+ 'a,
+ 'tcx,
+ impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>,
+ QueryResult<'tcx>,
+ > {
+ ProbeCtxt {
+ ecx: self,
+ probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate {
+ name: name.to_string(),
+ result: *result,
+ },
+ _result: PhantomData,
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
new file mode 100644
index 000000000..bf6cbef8c
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -0,0 +1,307 @@
+use std::ops::ControlFlow;
+
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::traits::util::supertraits;
+use rustc_infer::traits::{
+ Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult,
+};
+use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal};
+use rustc_middle::traits::{
+ ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData,
+ ObligationCause, SelectionError,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource};
+use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree};
+use crate::solve::inspect::ProofTreeBuilder;
+use crate::solve::search_graph::OverflowHandler;
+use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
+
+pub trait InferCtxtSelectExt<'tcx> {
+ fn select_in_new_trait_solver(
+ &self,
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>>;
+}
+
+impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> {
+ fn select_in_new_trait_solver(
+ &self,
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>> {
+ assert!(self.next_trait_solver());
+
+ let trait_goal = Goal::new(
+ self.tcx,
+ obligation.param_env,
+ self.instantiate_binder_with_placeholders(obligation.predicate),
+ );
+
+ let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| {
+ let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate);
+ let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal);
+ let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal);
+
+ // pseudo-winnow
+ if candidates.len() == 0 {
+ return Err(SelectionError::Unimplemented);
+ } else if candidates.len() > 1 {
+ let mut i = 0;
+ while i < candidates.len() {
+ let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
+ candidate_should_be_dropped_in_favor_of(
+ ecx.tcx(),
+ &candidates[i],
+ &candidates[j],
+ )
+ });
+ if should_drop_i {
+ candidates.swap_remove(i);
+ } else {
+ i += 1;
+ if i > 1 {
+ return Ok(None);
+ }
+ }
+ }
+ }
+
+ let candidate = candidates.pop().unwrap();
+ let (certainty, nested_goals) = ecx
+ .instantiate_and_apply_query_response(
+ trait_goal.param_env,
+ orig_values,
+ candidate.result,
+ )
+ .map_err(|_| SelectionError::Unimplemented)?;
+
+ Ok(Some((candidate, certainty, nested_goals)))
+ });
+
+ let (candidate, certainty, nested_goals) = match result {
+ Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals),
+ Ok(None) => return Ok(None),
+ Err(e) => return Err(e),
+ };
+
+ let nested_obligations: Vec<_> = nested_goals
+ .into_iter()
+ .map(|goal| {
+ Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate)
+ })
+ .collect();
+
+ let goal = self.resolve_vars_if_possible(trait_goal);
+ match (certainty, candidate.source) {
+ // Rematching the implementation will instantiate the same nested goals that
+ // would have caused the ambiguity, so we can still make progress here regardless.
+ (_, CandidateSource::Impl(def_id)) => {
+ rematch_impl(self, goal, def_id, nested_obligations)
+ }
+
+ // Rematching the dyn upcast or object goal will instantiate the same nested
+ // goals that would have caused the ambiguity, so we can still make progress here
+ // regardless.
+ // FIXME: This doesn't actually check the object bounds hold here.
+ (
+ _,
+ CandidateSource::BuiltinImpl(
+ BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting,
+ ),
+ ) => rematch_object(self, goal, nested_obligations),
+
+ // Technically some builtin impls have nested obligations, but if
+ // `Certainty::Yes`, then they should've all been verified and don't
+ // need re-checking.
+ (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => {
+ Ok(Some(ImplSource::Builtin(nested_obligations)))
+ }
+
+ // It's fine not to do anything to rematch these, since there are no
+ // nested obligations.
+ (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => {
+ Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst)))
+ }
+
+ (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity))
+ | (Certainty::Maybe(_), _) => Ok(None),
+ }
+ }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ fn compute_canonical_trait_candidates(
+ &mut self,
+ canonical_input: CanonicalInput<'tcx>,
+ ) -> Vec<Candidate<'tcx>> {
+ // This doesn't record the canonical goal on the stack during the
+ // candidate assembly step, but that's fine. Selection is conceptually
+ // outside of the solver, and if there were any cycles, we'd encounter
+ // the cycle anyways one step later.
+ EvalCtxt::enter_canonical(
+ self.tcx(),
+ self.search_graph(),
+ canonical_input,
+ // FIXME: This is wrong, idk if we even want to track stuff here.
+ &mut ProofTreeBuilder::new_noop(),
+ |ecx, goal| {
+ let trait_goal = Goal {
+ param_env: goal.param_env,
+ predicate: goal
+ .predicate
+ .to_opt_poly_trait_pred()
+ .expect("we canonicalized a trait goal")
+ .no_bound_vars()
+ .expect("we instantiated all bound vars"),
+ };
+ ecx.assemble_and_evaluate_candidates(trait_goal)
+ },
+ )
+ }
+}
+
+fn candidate_should_be_dropped_in_favor_of<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ victim: &Candidate<'tcx>,
+ other: &Candidate<'tcx>,
+) -> bool {
+ match (victim.source, other.source) {
+ (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => {
+ victim_idx >= other_idx
+ }
+ (_, CandidateSource::ParamEnv(_)) => true,
+
+ (
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
+ ) => false,
+ (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true,
+
+ (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => {
+ tcx.specializes((other_def_id, victim_def_id))
+ && other.result.value.certainty == Certainty::Yes
+ }
+
+ _ => false,
+ }
+}
+
+fn rematch_impl<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+ impl_def_id: DefId,
+ mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+ let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs);
+
+ nested.extend(
+ infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref)
+ .map_err(|_| SelectionError::Unimplemented)?
+ .into_obligations(),
+ );
+
+ nested.extend(
+ infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map(
+ |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred),
+ ),
+ );
+
+ Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested })))
+}
+
+fn rematch_object<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ goal: Goal<'tcx, ty::TraitPredicate<'tcx>>,
+ mut nested: Vec<PredicateObligation<'tcx>>,
+) -> SelectionResult<'tcx, Selection<'tcx>> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Dynamic(data, _, source_kind) = *self_ty.kind()
+ else {
+ bug!()
+ };
+ let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, self_ty);
+
+ let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id())
+ == infcx.tcx.lang_items().unsize_trait()
+ {
+ assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*");
+ if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() {
+ (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty))
+ } else {
+ bug!()
+ }
+ } else {
+ (false, ty::Binder::dummy(goal.predicate.trait_ref))
+ };
+
+ let mut target_trait_ref = None;
+ for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) {
+ let result = infcx.commit_if_ok(|_| {
+ infcx.at(&ObligationCause::dummy(), goal.param_env).eq(
+ DefineOpaqueTypes::No,
+ target_trait_ref_unnormalized,
+ candidate_trait_ref,
+ )
+
+ // FIXME: We probably should at least shallowly verify these...
+ });
+
+ match result {
+ Ok(InferOk { value: (), obligations }) => {
+ target_trait_ref = Some(candidate_trait_ref);
+ nested.extend(obligations);
+ break;
+ }
+ Err(_) => continue,
+ }
+ }
+
+ let target_trait_ref = target_trait_ref.unwrap();
+
+ let mut offset = 0;
+ let Some((vtable_base, vtable_vptr_slot)) =
+ prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| {
+ match segment {
+ VtblSegment::MetadataDSA => {
+ offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
+ }
+ VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
+ let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref);
+
+ if trait_ref == target_trait_ref {
+ if emit_vptr {
+ return ControlFlow::Break((
+ offset,
+ Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)),
+ ));
+ } else {
+ return ControlFlow::Break((offset, None));
+ }
+ }
+
+ offset += own_vtable_entries;
+ if emit_vptr {
+ offset += 1;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ })
+ else {
+ bug!();
+ };
+
+ // If we're upcasting, get the offset of the vtable pointer, otherwise get
+ // the base of the vtable.
+ Ok(Some(if is_upcasting {
+ ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
+ } else {
+ ImplSource::Object(ImplSourceObjectData { vtable_base, nested })
+ }))
+}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 4a403196c..88ee14c4d 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -10,6 +10,7 @@ use rustc_infer::traits::{
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use super::eval_ctxt::GenerateProofTree;
use super::{Certainty, InferCtxtEvalExt};
/// A trait engine using the new trait solver.
@@ -25,20 +26,28 @@ use super::{Certainty, InferCtxtEvalExt};
/// here as this will have to deal with far more root goals than `evaluate_all`.
pub struct FulfillmentCtxt<'tcx> {
obligations: Vec<PredicateObligation<'tcx>>,
+
+ /// The snapshot in which this context was created. Using the context
+ /// outside of this snapshot leads to subtle bugs if the snapshot
+ /// gets rolled back. Because of this we explicitly check that we only
+ /// use the context in exactly this snapshot.
+ usable_in_snapshot: usize,
}
impl<'tcx> FulfillmentCtxt<'tcx> {
- pub fn new() -> FulfillmentCtxt<'tcx> {
- FulfillmentCtxt { obligations: Vec::new() }
+ pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
+ FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
+ #[instrument(level = "debug", skip(self, infcx))]
fn register_predicate_obligation(
&mut self,
- _infcx: &InferCtxt<'tcx>,
+ infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
self.obligations.push(obligation);
}
@@ -46,8 +55,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
self.obligations
.drain(..)
.map(|obligation| {
- let code =
- infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) {
+ let code = infcx.probe(|_| {
+ match infcx
+ .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No)
+ .0
+ {
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
FulfillmentErrorCode::CodeAmbiguity { overflow: false }
}
@@ -60,7 +72,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
Err(_) => {
bug!("did not expect selection error when collecting ambiguity errors")
}
- });
+ }
+ });
FulfillmentError {
obligation: obligation.clone(),
@@ -72,6 +85,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
}
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
@@ -81,72 +95,61 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
let mut has_changed = false;
for obligation in mem::take(&mut self.obligations) {
let goal = obligation.clone().into();
- let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) {
- Ok(result) => result,
- Err(NoSolution) => {
- errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: match goal.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
- FulfillmentErrorCode::CodeProjectionError(
- // FIXME: This could be a `Sorts` if the term is a type
- MismatchedProjectionTypes { err: TypeError::Mismatch },
- )
- }
- ty::PredicateKind::AliasRelate(_, _, _) => {
- FulfillmentErrorCode::CodeProjectionError(
- MismatchedProjectionTypes { err: TypeError::Mismatch },
- )
- }
- ty::PredicateKind::Subtype(pred) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((pred.a, pred.b)),
- );
- let expected_found = ExpectedFound::new(true, a, b);
- FulfillmentErrorCode::CodeSubtypeError(
- expected_found,
- TypeError::Sorts(expected_found),
- )
- }
- ty::PredicateKind::Coerce(pred) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((pred.a, pred.b)),
- );
- let expected_found = ExpectedFound::new(false, a, b);
- FulfillmentErrorCode::CodeSubtypeError(
- expected_found,
- TypeError::Sorts(expected_found),
- )
- }
- ty::PredicateKind::ConstEquate(a, b) => {
- let (a, b) = infcx.instantiate_binder_with_placeholders(
- goal.predicate.kind().rebind((a, b)),
- );
- let expected_found = ExpectedFound::new(true, a, b);
- FulfillmentErrorCode::CodeConstEquateError(
- expected_found,
- TypeError::ConstMismatch(expected_found),
- )
- }
- ty::PredicateKind::Clause(_)
- | ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::Ambiguous => {
- FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- )
- }
- ty::PredicateKind::TypeWellFormedFromEnv(_) => {
- bug!("unexpected goal: {goal:?}")
- }
- },
- root_obligation: obligation,
- });
- continue;
- }
- };
+ let (changed, certainty, nested_goals) =
+ match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 {
+ Ok(result) => result,
+ Err(NoSolution) => {
+ errors.push(FulfillmentError {
+ obligation: obligation.clone(),
+ code: match goal.predicate.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+ FulfillmentErrorCode::CodeProjectionError(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::AliasRelate(_, _, _) => {
+ FulfillmentErrorCode::CodeProjectionError(
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::Subtype(pred) => {
+ let (a, b) = infcx.instantiate_binder_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(true, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::Coerce(pred) => {
+ let (a, b) = infcx.instantiate_binder_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(false, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::Clause(_)
+ | ty::PredicateKind::ObjectSafe(_)
+ | ty::PredicateKind::ClosureKind(_, _, _)
+ | ty::PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::CodeSelectionError(
+ SelectionError::Unimplemented,
+ )
+ }
+ ty::PredicateKind::ConstEquate(..) => {
+ bug!("unexpected goal: {goal:?}")
+ }
+ },
+ root_obligation: obligation,
+ });
+ continue;
+ }
+ };
// Push any nested goals that we get from unifying our canonical response
// with our obligation onto the fulfillment context.
self.obligations.extend(nested_goals.into_iter().map(|goal| {
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
new file mode 100644
index 000000000..2d6717fda
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -0,0 +1,435 @@
+use rustc_middle::traits::query::NoSolution;
+use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind};
+use rustc_middle::traits::solve::{
+ CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
+};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::config::DumpSolverProofTree;
+
+use super::eval_ctxt::UseGlobalCache;
+use super::GenerateProofTree;
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluation<'tcx> {
+ pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ pub canonicalized_goal: Option<CanonicalInput<'tcx>>,
+
+ pub evaluation_steps: Vec<WipGoalEvaluationStep<'tcx>>,
+
+ pub cache_hit: Option<CacheHit>,
+ pub is_normalizes_to_hack: IsNormalizesToHack,
+ pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+
+ pub result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipGoalEvaluation<'tcx> {
+ pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
+ inspect::GoalEvaluation {
+ uncanonicalized_goal: self.uncanonicalized_goal,
+ canonicalized_goal: self.canonicalized_goal.unwrap(),
+ kind: match self.cache_hit {
+ Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit),
+ None => inspect::GoalEvaluationKind::Uncached {
+ revisions: self
+ .evaluation_steps
+ .into_iter()
+ .map(WipGoalEvaluationStep::finalize)
+ .collect(),
+ },
+ },
+ is_normalizes_to_hack: self.is_normalizes_to_hack,
+ returned_goals: self.returned_goals,
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipAddedGoalsEvaluation<'tcx> {
+ pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>,
+ pub result: Option<Result<Certainty, NoSolution>>,
+}
+
+impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
+ pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> {
+ inspect::AddedGoalsEvaluation {
+ evaluations: self
+ .evaluations
+ .into_iter()
+ .map(|evaluations| {
+ evaluations.into_iter().map(WipGoalEvaluation::finalize).collect()
+ })
+ .collect(),
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalEvaluationStep<'tcx> {
+ pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+
+ pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<WipGoalCandidate<'tcx>>,
+
+ pub result: Option<QueryResult<'tcx>>,
+}
+
+impl<'tcx> WipGoalEvaluationStep<'tcx> {
+ pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> {
+ inspect::GoalEvaluationStep {
+ instantiated_goal: self.instantiated_goal,
+ nested_goal_evaluations: self
+ .nested_goal_evaluations
+ .into_iter()
+ .map(WipAddedGoalsEvaluation::finalize)
+ .collect(),
+ candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+ result: self.result.unwrap(),
+ }
+ }
+}
+
+#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
+pub struct WipGoalCandidate<'tcx> {
+ pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
+ pub candidates: Vec<WipGoalCandidate<'tcx>>,
+ pub kind: Option<CandidateKind<'tcx>>,
+}
+
+impl<'tcx> WipGoalCandidate<'tcx> {
+ pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
+ inspect::GoalCandidate {
+ nested_goal_evaluations: self
+ .nested_goal_evaluations
+ .into_iter()
+ .map(WipAddedGoalsEvaluation::finalize)
+ .collect(),
+ candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+ kind: self.kind.unwrap(),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum DebugSolver<'tcx> {
+ Root,
+ GoalEvaluation(WipGoalEvaluation<'tcx>),
+ AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
+ GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
+ GoalCandidate(WipGoalCandidate<'tcx>),
+}
+
+impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalEvaluation(g)
+ }
+}
+
+impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::AddedGoalsEvaluation(g)
+ }
+}
+
+impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalEvaluationStep(g)
+ }
+}
+
+impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
+ fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> {
+ DebugSolver::GoalCandidate(g)
+ }
+}
+
+pub struct ProofTreeBuilder<'tcx> {
+ state: Option<Box<BuilderData<'tcx>>>,
+}
+
+struct BuilderData<'tcx> {
+ tree: DebugSolver<'tcx>,
+ use_global_cache: UseGlobalCache,
+}
+
+impl<'tcx> ProofTreeBuilder<'tcx> {
+ fn new(
+ state: impl Into<DebugSolver<'tcx>>,
+ use_global_cache: UseGlobalCache,
+ ) -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder {
+ state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })),
+ }
+ }
+
+ fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self {
+ match &self.state {
+ Some(prev_state) => Self {
+ state: Some(Box::new(BuilderData {
+ tree: state.into(),
+ use_global_cache: prev_state.use_global_cache,
+ })),
+ },
+ None => Self { state: None },
+ }
+ }
+
+ fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
+ self.state.as_mut().map(|boxed| &mut boxed.tree)
+ }
+
+ pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
+ match self.state?.tree {
+ DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
+ Some(wip_goal_evaluation.finalize())
+ }
+ root => unreachable!("unexpected proof tree builder root node: {:?}", root),
+ }
+ }
+
+ pub fn use_global_cache(&self) -> bool {
+ self.state
+ .as_ref()
+ .map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes))
+ .unwrap_or(true)
+ }
+
+ pub fn new_maybe_root(
+ tcx: TyCtxt<'tcx>,
+ generate_proof_tree: GenerateProofTree,
+ ) -> ProofTreeBuilder<'tcx> {
+ let generate_proof_tree = match (
+ tcx.sess.opts.unstable_opts.dump_solver_proof_tree,
+ tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache,
+ generate_proof_tree,
+ ) {
+ (_, Some(use_cache), GenerateProofTree::Yes(_)) => {
+ GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+ }
+
+ (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => {
+ let use_cache = use_cache.unwrap_or(true);
+ GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache))
+ }
+
+ (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree,
+ (DumpSolverProofTree::Never, _, _) => generate_proof_tree,
+ (DumpSolverProofTree::OnError, _, _) => generate_proof_tree,
+ };
+
+ match generate_proof_tree {
+ GenerateProofTree::No => ProofTreeBuilder::new_noop(),
+ GenerateProofTree::Yes(global_cache_disabled) => {
+ ProofTreeBuilder::new_root(global_cache_disabled)
+ }
+ }
+ }
+
+ pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder::new(DebugSolver::Root, use_global_cache)
+ }
+
+ pub fn new_noop() -> ProofTreeBuilder<'tcx> {
+ ProofTreeBuilder { state: None }
+ }
+
+ pub fn is_noop(&self) -> bool {
+ self.state.is_none()
+ }
+
+ pub fn new_goal_evaluation(
+ &mut self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ is_normalizes_to_hack: IsNormalizesToHack,
+ ) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalEvaluation {
+ uncanonicalized_goal: goal,
+ canonicalized_goal: None,
+ evaluation_steps: vec![],
+ is_normalizes_to_hack,
+ cache_hit: None,
+ returned_goals: vec![],
+ result: None,
+ })
+ }
+
+ pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn cache_hit(&mut self, cache_hit: CacheHit) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None);
+ }
+ _ => unreachable!(),
+ };
+ }
+ }
+
+ pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(evaluation) => {
+ assert!(evaluation.returned_goals.is_empty());
+ evaluation.returned_goals.extend(goals);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+ pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goal_evaluation.state.unwrap().tree) {
+ (
+ DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
+ evaluations, ..
+ }),
+ DebugSolver::GoalEvaluation(goal_evaluation),
+ ) => evaluations.last_mut().unwrap().push(goal_evaluation),
+ (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation,
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_goal_evaluation_step(
+ &mut self,
+ instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
+ ) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalEvaluationStep {
+ instantiated_goal,
+ nested_goal_evaluations: vec![],
+ candidates: vec![],
+ result: None,
+ })
+ }
+ pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goal_eval_step.state.unwrap().tree) {
+ (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => {
+ goal_eval.evaluation_steps.push(step);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipGoalCandidate {
+ nested_goal_evaluations: vec![],
+ candidates: vec![],
+ kind: None,
+ })
+ }
+
+ pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalCandidate(this) => {
+ assert_eq!(this.kind.replace(candidate_kind), None)
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, candidate.state.unwrap().tree) {
+ (
+ DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
+ | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }),
+ DebugSolver::GoalCandidate(candidate),
+ ) => candidates.push(candidate),
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
+ if self.state.is_none() {
+ return ProofTreeBuilder { state: None };
+ }
+
+ self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None })
+ }
+
+ pub fn evaluate_added_goals_loop_start(&mut self) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::AddedGoalsEvaluation(this) => {
+ this.evaluations.push(vec![]);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::AddedGoalsEvaluation(this) => {
+ assert_eq!(this.result.replace(result), None);
+ }
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match (this, goals_evaluation.state.unwrap().tree) {
+ (
+ DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+ nested_goal_evaluations,
+ ..
+ })
+ | DebugSolver::GoalCandidate(WipGoalCandidate {
+ nested_goal_evaluations, ..
+ }),
+ DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
+ ) => nested_goal_evaluations.push(added_goals_evaluation),
+ _ => unreachable!(),
+ }
+ }
+ }
+
+ pub fn query_result(&mut self, result: QueryResult<'tcx>) {
+ if let Some(this) = self.as_mut() {
+ match this {
+ DebugSolver::GoalEvaluation(goal_evaluation) => {
+ assert_eq!(goal_evaluation.result.replace(result), None);
+ }
+ DebugSolver::GoalEvaluationStep(evaluation_step) => {
+ assert_eq!(evaluation_step.result.replace(result), None);
+ }
+ DebugSolver::Root
+ | DebugSolver::AddedGoalsEvaluation(_)
+ | DebugSolver::GoalCandidate(_) => unreachable!(),
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 56a254d9c..77809d8d2 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -20,17 +20,24 @@ use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
};
+mod alias_relate;
mod assembly;
mod canonicalize;
mod eval_ctxt;
mod fulfill;
+pub mod inspect;
+mod normalize;
mod opaques;
mod project_goals;
mod search_graph;
mod trait_goals;
+mod weak_types;
-pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt};
+pub use eval_ctxt::{
+ EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
+};
pub use fulfill::FulfillmentCtxt;
+pub(crate) use normalize::deeply_normalize;
#[derive(Debug, Clone, Copy)]
enum SolverMode {
@@ -154,139 +161,40 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
}
}
- #[instrument(level = "debug", skip(self), ret)]
- fn compute_alias_relate_goal(
+ #[instrument(level = "debug", skip(self))]
+ fn compute_const_evaluatable_goal(
&mut self,
- goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+ Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
) -> QueryResult<'tcx> {
- let tcx = self.tcx();
- // We may need to invert the alias relation direction if dealing an alias on the RHS.
- #[derive(Debug)]
- enum Invert {
- No,
- Yes,
- }
- let evaluate_normalizes_to =
- |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| {
- let span = tracing::span!(
- tracing::Level::DEBUG,
- "compute_alias_relate_goal(evaluate_normalizes_to)",
- ?alias,
- ?other,
- ?direction,
- ?invert
- );
- let _enter = span.enter();
- let result = ecx.probe(|ecx| {
- let other = match direction {
- // This is purely an optimization.
- ty::AliasRelationDirection::Equate => other,
-
- ty::AliasRelationDirection::Subtype => {
- let fresh = ecx.next_term_infer_of_kind(other);
- let (sub, sup) = match invert {
- Invert::No => (fresh, other),
- Invert::Yes => (other, fresh),
- };
- ecx.sub(goal.param_env, sub, sup)?;
- fresh
- }
- };
- ecx.add_goal(goal.with(
- tcx,
- ty::Binder::dummy(ty::ProjectionPredicate {
- projection_ty: alias,
- term: other,
- }),
- ));
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- });
- debug!(?result);
- result
- };
-
- let (lhs, rhs, direction) = goal.predicate;
-
- if lhs.is_infer() || rhs.is_infer() {
- bug!(
- "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
- );
- }
-
- match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
- (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
-
- // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
- (Some(alias_lhs), None) => {
- evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No)
- }
-
- // LHS is not a projection, only way this is true is if RHS normalizes-to LHS
- (None, Some(alias_rhs)) => {
- evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes)
- }
-
- (Some(alias_lhs), Some(alias_rhs)) => {
- debug!("both sides are aliases");
-
- let mut candidates = Vec::new();
- // LHS normalizes-to RHS
- candidates.extend(evaluate_normalizes_to(
- self,
- alias_lhs,
- rhs,
- direction,
- Invert::No,
- ));
- // RHS normalizes-to RHS
- candidates.extend(evaluate_normalizes_to(
- self,
- alias_rhs,
- lhs,
- direction,
- Invert::Yes,
- ));
- // Relate via substs
- let subst_relate_response = self.probe(|ecx| {
- let span = tracing::span!(
- tracing::Level::DEBUG,
- "compute_alias_relate_goal(relate_via_substs)",
- ?alias_lhs,
- ?alias_rhs,
- ?direction
- );
- let _enter = span.enter();
-
- match direction {
- ty::AliasRelationDirection::Equate => {
- ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
- }
- ty::AliasRelationDirection::Subtype => {
- ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
- }
- }
-
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- });
- candidates.extend(subst_relate_response);
- debug!(?candidates);
-
- if let Some(merged) = self.try_merge_responses(&candidates) {
- Ok(merged)
+ match ct.kind() {
+ ty::ConstKind::Unevaluated(uv) => {
+ // We never return `NoSolution` here as `try_const_eval_resolve` emits an
+ // error itself when failing to evaluate, so emitting an additional fulfillment
+ // error in that case is unnecessary noise. This may change in the future once
+ // evaluation failures are allowed to impact selection, e.g. generic const
+ // expressions in impl headers or `where`-clauses.
+
+ // FIXME(generic_const_exprs): Implement handling for generic
+ // const expressions here.
+ if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} else {
- // When relating two aliases and we have ambiguity, we prefer
- // relating the generic arguments of the aliases over normalizing
- // them. This is necessary for inference during typeck.
- //
- // As this is incomplete, we must not do so during coherence.
- match (self.solver_mode(), subst_relate_response) {
- (SolverMode::Normal, Ok(response)) => Ok(response),
- (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => {
- self.flounder(&candidates)
- }
- }
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
+ ty::ConstKind::Infer(_) => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+ // We can freely ICE here as:
+ // - `Param` gets replaced with a placeholder during canonicalization
+ // - `Bound` cannot exist as we don't have a binder around the self Type
+ // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
+ ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
+ bug!("unexpect const kind: {:?}", ct)
+ }
}
}
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
new file mode 100644
index 000000000..c388850d8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -0,0 +1,220 @@
+use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::traits::TraitEngineExt;
+use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::traits::Reveal;
+use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
+
+use super::FulfillmentCtxt;
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
+ at: At<'_, 'tcx>,
+ value: T,
+) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+ let fulfill_cx = FulfillmentCtxt::new(at.infcx);
+ let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() };
+
+ value.try_fold_with(&mut folder)
+}
+
+struct NormalizationFolder<'me, 'tcx> {
+ at: At<'me, 'tcx>,
+ fulfill_cx: FulfillmentCtxt<'tcx>,
+ depth: usize,
+ universes: Vec<Option<UniverseIndex>>,
+}
+
+impl<'tcx> NormalizationFolder<'_, 'tcx> {
+ fn normalize_alias_ty(
+ &mut self,
+ alias: AliasTy<'tcx>,
+ ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
+ let infcx = self.at.infcx;
+ let tcx = infcx.tcx;
+ let recursion_limit = tcx.recursion_limit();
+ if !recursion_limit.value_within_limit(self.depth) {
+ self.at.infcx.err_ctxt().report_overflow_error(
+ &alias.to_ty(tcx),
+ self.at.cause.span,
+ true,
+ |_| {},
+ );
+ }
+
+ self.depth += 1;
+
+ let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::NormalizeProjectionType,
+ span: self.at.cause.span,
+ });
+ let obligation = Obligation::new(
+ tcx,
+ self.at.cause.clone(),
+ self.at.param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: alias,
+ term: new_infer_ty.into(),
+ }),
+ );
+
+ // Do not emit an error if normalization is known to fail but instead
+ // keep the projection unnormalized. This is the case for projections
+ // with a `T: Trait` where-clause and opaque types outside of the defining
+ // scope.
+ let result = if infcx.predicate_may_hold(&obligation) {
+ self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+ let errors = self.fulfill_cx.select_all_or_error(infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+ let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+ ty.try_fold_with(self)?
+ } else {
+ alias.to_ty(tcx).try_super_fold_with(self)?
+ };
+
+ self.depth -= 1;
+ Ok(result)
+ }
+
+ fn normalize_unevaluated_const(
+ &mut self,
+ ty: Ty<'tcx>,
+ uv: ty::UnevaluatedConst<'tcx>,
+ ) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
+ let infcx = self.at.infcx;
+ let tcx = infcx.tcx;
+ let recursion_limit = tcx.recursion_limit();
+ if !recursion_limit.value_within_limit(self.depth) {
+ self.at.infcx.err_ctxt().report_overflow_error(
+ &ty::Const::new_unevaluated(tcx, uv, ty),
+ self.at.cause.span,
+ true,
+ |_| {},
+ );
+ }
+
+ self.depth += 1;
+
+ let new_infer_ct = infcx.next_const_var(
+ ty,
+ ConstVariableOrigin {
+ kind: ConstVariableOriginKind::MiscVariable,
+ span: self.at.cause.span,
+ },
+ );
+ let obligation = Obligation::new(
+ tcx,
+ self.at.cause.clone(),
+ self.at.param_env,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: tcx.mk_alias_ty(uv.def, uv.substs),
+ term: new_infer_ct.into(),
+ }),
+ );
+
+ let result = if infcx.predicate_may_hold(&obligation) {
+ self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+ let errors = self.fulfill_cx.select_all_or_error(infcx);
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+ let ct = infcx.resolve_vars_if_possible(new_infer_ct);
+ ct.try_fold_with(self)?
+ } else {
+ ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)?
+ };
+
+ self.depth -= 1;
+ Ok(result)
+ }
+}
+
+impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
+ type Error = Vec<FulfillmentError<'tcx>>;
+
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.at.infcx.tcx
+ }
+
+ fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+ &mut self,
+ t: ty::Binder<'tcx, T>,
+ ) -> Result<ty::Binder<'tcx, T>, Self::Error> {
+ self.universes.push(None);
+ let t = t.try_super_fold_with(self)?;
+ self.universes.pop();
+ Ok(t)
+ }
+
+ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ let reveal = self.at.param_env.reveal();
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ty, infcx.shallow_resolve(ty));
+ if !needs_normalization(&ty, reveal) {
+ return Ok(ty);
+ }
+
+ // We don't normalize opaque types unless we have
+ // `Reveal::All`, even if we're in the defining scope.
+ let data = match *ty.kind() {
+ ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty,
+ _ => return ty.try_super_fold_with(self),
+ };
+
+ if data.has_escaping_bound_vars() {
+ let (data, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+ let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?;
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &mut self.universes,
+ result,
+ ))
+ } else {
+ ensure_sufficient_stack(|| self.normalize_alias_ty(data))
+ }
+ }
+
+ fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+ let reveal = self.at.param_env.reveal();
+ let infcx = self.at.infcx;
+ debug_assert_eq!(ct, infcx.shallow_resolve(ct));
+ if !needs_normalization(&ct, reveal) {
+ return Ok(ct);
+ }
+
+ let uv = match ct.kind() {
+ ty::ConstKind::Unevaluated(ct) => ct,
+ _ => return ct.try_super_fold_with(self),
+ };
+
+ if uv.has_escaping_bound_vars() {
+ let (uv, mapped_regions, mapped_types, mapped_consts) =
+ BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv);
+ let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?;
+ Ok(PlaceholderReplacer::replace_placeholders(
+ infcx,
+ mapped_regions,
+ mapped_types,
+ mapped_consts,
+ &mut self.universes,
+ result,
+ ))
+ } else {
+ ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))
+ }
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs
index a5de4ddee..16194f5ad 100644
--- a/compiler/rustc_trait_selection/src/solve/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/opaques.rs
@@ -20,8 +20,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
return Err(NoSolution);
};
- let opaque_ty =
- ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
// FIXME: at some point we should call queries without defining
// new opaque types but having the existing opaque type definitions.
// This will require moving this below "Prefer opaques registered already".
@@ -41,7 +39,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(()) => {}
}
// Prefer opaques registered already.
- let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected);
+ let opaque_type_key =
+ ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
+ let matches =
+ self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected);
if !matches.is_empty() {
if let Some(response) = self.try_merge_responses(&matches) {
return Ok(response);
@@ -50,10 +51,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
// Otherwise, define a new opaque type
- self.register_opaque_ty(opaque_ty, expected, goal.param_env)?;
+ self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.substs,
+ goal.param_env,
+ expected,
+ );
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
(Reveal::UserFacing, SolverMode::Coherence) => {
+ // An impossible opaque type bound is the only way this goal will fail
+ // e.g. assigning `impl Copy := NotCopy`
+ self.add_item_bounds_for_hidden_type(
+ opaque_ty.def_id,
+ opaque_ty.substs,
+ goal.param_env,
+ expected,
+ );
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
(Reveal::All, _) => {
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 7d7dfa2c8..e53b784a7 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -9,6 +9,7 @@ use rustc_hir::LangItem;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::specialization_graph::LeafDef;
use rustc_infer::traits::Reveal;
+use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::ProjectionPredicate;
@@ -22,25 +23,66 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- match goal.predicate.projection_ty.kind(self.tcx()) {
- ty::AliasKind::Projection => {
+ let def_id = goal.predicate.def_id();
+ match self.tcx().def_kind(def_id) {
+ DefKind::AssocTy | DefKind::AssocConst => {
// To only compute normalization once for each projection we only
- // normalize if the expected term is an unconstrained inference variable.
+ // assemble normalization candidates if the expected term is an
+ // unconstrained inference variable.
+ //
+ // Why: For better cache hits, since if we have an unconstrained RHS then
+ // there are only as many cache keys as there are (canonicalized) alias
+ // types in each normalizes-to goal. This also weakens inference in a
+ // forwards-compatible way so we don't use the value of the RHS term to
+ // affect candidate assembly for projections.
//
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
// `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
// `U` and equate it with `u32`. This means that we don't need a separate
- // projection cache in the solver.
+ // projection cache in the solver, since we're piggybacking off of regular
+ // goal caching.
if self.term_is_fully_unconstrained(goal) {
- let candidates = self.assemble_and_evaluate_candidates(goal);
- self.merge_candidates(candidates)
+ match self.tcx().associated_item(def_id).container {
+ ty::AssocItemContainer::TraitContainer => {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_candidates(candidates)
+ }
+ ty::AssocItemContainer::ImplContainer => {
+ bug!("IATs not supported here yet")
+ }
+ }
} else {
self.set_normalizes_to_hack_goal(goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
- ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
- ty::AliasKind::Inherent => bug!("IATs not supported here yet"),
+ DefKind::AnonConst => self.normalize_anon_const(goal),
+ DefKind::OpaqueTy => self.normalize_opaque_type(goal),
+ DefKind::TyAlias => self.normalize_weak_type(goal),
+ kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn normalize_anon_const(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ if let Some(normalized_const) = self.try_const_eval_resolve(
+ goal.param_env,
+ ty::UnevaluatedConst::new(
+ goal.predicate.projection_ty.def_id,
+ goal.predicate.projection_ty.substs,
+ ),
+ self.tcx()
+ .type_of(goal.predicate.projection_ty.def_id)
+ .no_bound_vars()
+ .expect("const ty should not rely on other generics"),
+ ) {
+ self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ } else {
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
}
@@ -65,24 +107,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
- && poly_projection_pred.projection_def_id() == goal.predicate.def_id()
- {
- ecx.probe(|ecx| {
- let assumption_projection_pred =
- ecx.instantiate_binder_with_infer(poly_projection_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.projection_ty,
- assumption_projection_pred.projection_ty,
- )?;
- ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
- .expect("expected goal term to be fully unconstrained");
- then(ecx)
- })
+ if let Some(projection_pred) = assumption.as_projection_clause() {
+ if projection_pred.projection_def_id() == goal.predicate.def_id() {
+ ecx.probe_candidate("assumption").enter(|ecx| {
+ let assumption_projection_pred =
+ ecx.instantiate_binder_with_infer(projection_pred);
+ ecx.eq(
+ goal.param_env,
+ goal.predicate.projection_ty,
+ assumption_projection_pred.projection_ty,
+ )?;
+ ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
+ .expect("expected goal term to be fully unconstrained");
+ then(ecx)
+ })
+ } else {
+ Err(NoSolution)
+ }
} else {
Err(NoSolution)
}
@@ -102,101 +146,100 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
return Err(NoSolution);
}
- ecx.probe(|ecx| {
- let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
- let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
-
- ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
-
- let where_clause_bounds = tcx
- .predicates_of(impl_def_id)
- .instantiate(tcx, impl_substs)
- .predicates
- .into_iter()
- .map(|pred| goal.with(tcx, pred));
- ecx.add_goals(where_clause_bounds);
-
- // In case the associated item is hidden due to specialization, we have to
- // return ambiguity this would otherwise be incomplete, resulting in
- // unsoundness during coherence (#105782).
- let Some(assoc_def) = fetch_eligible_assoc_item_def(
- ecx,
- goal.param_env,
- goal_trait_ref,
- goal.predicate.def_id(),
- impl_def_id
- )? else {
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
- };
+ ecx.probe(
+ |r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter(
+ |ecx| {
+ let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
+ let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+
+ ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
+
+ let where_clause_bounds = tcx
+ .predicates_of(impl_def_id)
+ .instantiate(tcx, impl_substs)
+ .predicates
+ .into_iter()
+ .map(|pred| goal.with(tcx, pred));
+ ecx.add_goals(where_clause_bounds);
+
+ // In case the associated item is hidden due to specialization, we have to
+ // return ambiguity this would otherwise be incomplete, resulting in
+ // unsoundness during coherence (#105782).
+ let Some(assoc_def) = fetch_eligible_assoc_item_def(
+ ecx,
+ goal.param_env,
+ goal_trait_ref,
+ goal.predicate.def_id(),
+ impl_def_id
+ )? else {
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+ };
- if !assoc_def.item.defaultness(tcx).has_value() {
- let guar = tcx.sess.delay_span_bug(
- tcx.def_span(assoc_def.item.def_id),
- "missing value for assoc item in impl",
- );
- let error_term = match assoc_def.item.kind {
- ty::AssocKind::Const => tcx
- .const_error(
+ if !assoc_def.item.defaultness(tcx).has_value() {
+ let guar = tcx.sess.delay_span_bug(
+ tcx.def_span(assoc_def.item.def_id),
+ "missing value for assoc item in impl",
+ );
+ let error_term = match assoc_def.item.kind {
+ ty::AssocKind::Const => ty::Const::new_error(tcx,
+ guar,
tcx.type_of(goal.predicate.def_id())
.subst(tcx, goal.predicate.projection_ty.substs),
- guar,
- )
- .into(),
- ty::AssocKind::Type => tcx.ty_error(guar).into(),
- ty::AssocKind::Fn => unreachable!(),
- };
- ecx.eq(goal.param_env, goal.predicate.term, error_term)
- .expect("expected goal term to be fully unconstrained");
- return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
- }
+ )
+ .into(),
+ ty::AssocKind::Type => Ty::new_error(tcx,guar).into(),
+ ty::AssocKind::Fn => unreachable!(),
+ };
+ ecx.eq(goal.param_env, goal.predicate.term, error_term)
+ .expect("expected goal term to be fully unconstrained");
+ return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+ }
- // Getting the right substitutions here is complex, e.g. given:
- // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
- // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
- // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
- //
- // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
- // to `[u32, u64]`.
- //
- // And then map these substs to the substs of the defining impl of `Assoc`, going
- // from `[u32, u64]` to `[u32, i32, u64]`.
- let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
- tcx,
- goal_trait_ref.def_id,
- impl_substs,
- );
- let substs = ecx.translate_substs(
- goal.param_env,
- impl_def_id,
- impl_substs_with_gat,
- assoc_def.defining_node,
- );
-
- // Finally we construct the actual value of the associated type.
- let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
- let ty = tcx.type_of(assoc_def.item.def_id);
- let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
- let identity_substs =
- ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
- let did = assoc_def.item.def_id;
- let kind =
- ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
- ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
- } else {
- ty.map_bound(|ty| ty.into())
- };
+ // Getting the right substitutions here is complex, e.g. given:
+ // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+ // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+ // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+ //
+ // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
+ // to `[u32, u64]`.
+ //
+ // And then map these substs to the substs of the defining impl of `Assoc`, going
+ // from `[u32, u64]` to `[u32, i32, u64]`.
+ let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
+ tcx,
+ goal_trait_ref.def_id,
+ impl_substs,
+ );
+ let substs = ecx.translate_substs(
+ goal.param_env,
+ impl_def_id,
+ impl_substs_with_gat,
+ assoc_def.defining_node,
+ );
- ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
- .expect("expected goal term to be fully unconstrained");
- ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
- })
+ // Finally we construct the actual value of the associated type.
+ let term = match assoc_def.item.kind {
+ ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()),
+ ty::AssocKind::Const => bug!("associated const projection is not supported yet"),
+ ty::AssocKind::Fn => unreachable!("we should never project to a fn"),
+ };
+
+ ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
+ .expect("expected goal term to be fully unconstrained");
+ ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ },
+ )
}
fn consider_auto_trait_candidate(
- _ecx: &mut EvalCtxt<'_, 'tcx>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
- bug!("auto traits do not have associated types: {:?}", goal);
+ ecx.tcx().sess.delay_span_bug(
+ ecx.tcx().def_span(goal.predicate.def_id()),
+ "associated types not allowed on auto traits",
+ );
+ Err(NoSolution)
}
fn consider_trait_alias_candidate(
@@ -280,7 +323,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
let tcx = ecx.tcx();
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin pointee").enter(|ecx| {
let metadata_ty = match goal.predicate.self_ty().kind() {
ty::Bool
| ty::Char
@@ -300,7 +343,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
| ty::Never
| ty::Foreign(..) => tcx.types.unit,
- ty::Error(e) => tcx.ty_error(*e),
+ ty::Error(e) => Ty::new_error(tcx, *e),
ty::Str | ty::Slice(_) => tcx.types.usize,
@@ -323,7 +366,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
}
ty::Adt(def, substs) if def.is_struct() => {
- match def.non_enum_variant().fields.raw.last() {
+ match def.non_enum_variant().tail_opt() {
None => tcx.types.unit,
Some(field_def) => {
let self_ty = field_def.ty(tcx, substs);
@@ -497,7 +540,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
),
};
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin discriminant kind").enter(|ecx| {
ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into())
.expect("expected goal term to be fully unconstrained");
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index 19e4b2300..f00456e26 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -2,6 +2,7 @@ mod cache;
mod overflow;
pub(super) use overflow::OverflowHandler;
+use rustc_middle::traits::solve::inspect::CacheHit;
use self::cache::ProvisionalEntry;
use cache::ProvisionalCache;
@@ -12,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe
use rustc_middle::ty::TyCtxt;
use std::{collections::hash_map::Entry, mem};
+use super::inspect::ProofTreeBuilder;
use super::SolverMode;
rustc_index::newtype_index! {
@@ -88,11 +90,12 @@ impl<'tcx> SearchGraph<'tcx> {
/// Tries putting the new goal on the stack, returning an error if it is already cached.
///
/// This correctly updates the provisional cache if there is a cycle.
- #[instrument(level = "debug", skip(self, tcx), ret)]
+ #[instrument(level = "debug", skip(self, tcx, inspect), ret)]
fn try_push_stack(
&mut self,
tcx: TyCtxt<'tcx>,
input: CanonicalInput<'tcx>,
+ inspect: &mut ProofTreeBuilder<'tcx>,
) -> Result<(), QueryResult<'tcx>> {
// Look at the provisional cache to check for cycles.
let cache = &mut self.provisional_cache;
@@ -119,6 +122,8 @@ impl<'tcx> SearchGraph<'tcx> {
// Finally we can return either the provisional response for that goal if we have a
// coinductive cycle or an ambiguous result if the cycle is inductive.
Entry::Occupied(entry_index) => {
+ inspect.cache_hit(CacheHit::Provisional);
+
let entry_index = *entry_index.get();
let stack_depth = cache.depth(entry_index);
@@ -205,16 +210,18 @@ impl<'tcx> SearchGraph<'tcx> {
&mut self,
tcx: TyCtxt<'tcx>,
canonical_input: CanonicalInput<'tcx>,
- mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
+ inspect: &mut ProofTreeBuilder<'tcx>,
+ mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if self.should_use_global_cache() {
+ if self.should_use_global_cache() && inspect.use_global_cache() {
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
debug!(?canonical_input, ?result, "cache hit");
+ inspect.cache_hit(CacheHit::Global);
return result;
}
}
- match self.try_push_stack(tcx, canonical_input) {
+ match self.try_push_stack(tcx, canonical_input, inspect) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
@@ -231,7 +238,7 @@ impl<'tcx> SearchGraph<'tcx> {
result
},
|this| {
- let result = loop_body(this);
+ let result = loop_body(this, inspect);
this.try_finalize_goal(canonical_input, result).then(|| result)
},
)
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index f722f2813..ef5f25b1f 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -7,6 +7,7 @@ use rustc_hir::{LangItem, Movability};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::supertraits;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
+use rustc_middle::traits::Reveal;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
@@ -61,7 +62,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
},
};
- ecx.probe(|ecx| {
+ ecx.probe_candidate("impl").enter(|ecx| {
let impl_substs = ecx.fresh_substs_for_item(impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
@@ -81,24 +82,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
- assumption: ty::Predicate<'tcx>,
+ assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
- if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
- && poly_trait_pred.def_id() == goal.predicate.def_id()
- && poly_trait_pred.polarity() == goal.predicate.polarity
- {
- // FIXME: Constness
- ecx.probe(|ecx| {
- let assumption_trait_pred =
- ecx.instantiate_binder_with_infer(poly_trait_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.trait_ref,
- assumption_trait_pred.trait_ref,
- )?;
- then(ecx)
- })
+ if let Some(trait_clause) = assumption.as_trait_clause() {
+ if trait_clause.def_id() == goal.predicate.def_id()
+ && trait_clause.polarity() == goal.predicate.polarity
+ {
+ // FIXME: Constness
+ ecx.probe_candidate("assumption").enter(|ecx| {
+ let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+ ecx.eq(
+ goal.param_env,
+ goal.predicate.trait_ref,
+ assumption_trait_pred.trait_ref,
+ )?;
+ then(ecx)
+ })
+ } else {
+ Err(NoSolution)
+ }
} else {
Err(NoSolution)
}
@@ -116,6 +119,32 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return result;
}
+ // Don't call `type_of` on a local TAIT that's in the defining scope,
+ // since that may require calling `typeck` on the same item we're
+ // currently type checking, which will result in a fatal cycle that
+ // ideally we want to avoid, since we can make progress on this goal
+ // via an alias bound or a locally-inferred hidden type instead.
+ //
+ // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since
+ // we already normalize the self type in
+ // `assemble_candidates_after_normalizing_self_ty`, and we'd
+ // just be registering an identical candidate here.
+ //
+ // Returning `Err(NoSolution)` here is ok in `SolverMode::Coherence`
+ // since we'll always be registering an ambiguous candidate in
+ // `assemble_candidates_after_normalizing_self_ty` due to normalizing
+ // the TAIT.
+ if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
+ if matches!(goal.param_env.reveal(), Reveal::All)
+ || opaque_ty
+ .def_id
+ .as_local()
+ .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
+ {
+ return Err(NoSolution);
+ }
+ }
+
ecx.probe_and_evaluate_goal_for_constituent_tys(
goal,
structural_traits::instantiate_constituent_tys_for_auto_trait,
@@ -132,7 +161,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let tcx = ecx.tcx();
- ecx.probe(|ecx| {
+ ecx.probe_candidate("trait alias").enter(|ecx| {
let nested_obligations = tcx
.predicates_of(goal.predicate.def_id())
.instantiate(tcx, goal.predicate.trait_ref.substs);
@@ -344,7 +373,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
if b_ty.is_ty_var() {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}
- ecx.probe(|ecx| {
+ ecx.probe_candidate("builtin unsize").enter(|ecx| {
match (a_ty.kind(), b_ty.kind()) {
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
@@ -396,12 +425,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
return Err(NoSolution);
}
- let tail_field = a_def
- .non_enum_variant()
- .fields
- .raw
- .last()
- .expect("expected unsized ADT to have a tail field");
+ let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
@@ -414,7 +438,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
}));
- let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
+ let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_substs);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
@@ -434,7 +458,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
- tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied());
+ Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
@@ -476,7 +500,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
}
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
- ecx.probe(|ecx| -> Result<_, NoSolution> {
+ ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
@@ -493,7 +517,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
.map(ty::Binder::dummy),
);
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
- let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
+ let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
@@ -618,7 +642,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
| ty::Placeholder(..) => Some(Err(NoSolution)),
ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
@@ -698,7 +722,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
goal: Goal<'tcx, TraitPredicate<'tcx>>,
constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
) -> QueryResult<'tcx> {
- self.probe(|ecx| {
+ self.probe_candidate("constituent tys").enter(|ecx| {
ecx.add_goals(
constituent_tys(ecx, goal.predicate.self_ty())?
.into_iter()
diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs
new file mode 100644
index 000000000..b095b54c5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs
@@ -0,0 +1,19 @@
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+use super::EvalCtxt;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn normalize_weak_type(
+ &mut self,
+ goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let tcx = self.tcx();
+ let weak_ty = goal.predicate.projection_ty;
+ let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
+
+ let actual = tcx.type_of(weak_ty.def_id).subst(tcx, weak_ty.substs);
+ self.eq(goal.param_env, expected, actual)?;
+ self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 62d2aad52..cb38d0ac8 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -95,7 +95,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
tcx,
ObligationCause::dummy(),
orig_env,
- ty::Binder::dummy(ty::TraitPredicate {
+ ty::TraitPredicate {
trait_ref,
constness: ty::BoundConstness::NotConst,
polarity: if polarity {
@@ -103,7 +103,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
} else {
ImplPolarity::Negative
},
- }),
+ },
));
if let Ok(Some(ImplSource::UserDefined(_))) = result {
debug!(
@@ -255,7 +255,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// that are already in the `ParamEnv` (modulo regions): we already
// know that they must hold.
for predicate in param_env.caller_bounds() {
- fresh_preds.insert(self.clean_pred(infcx, predicate));
+ fresh_preds.insert(self.clean_pred(infcx, predicate.as_predicate()));
}
let mut select = SelectionContext::new(&infcx);
@@ -270,8 +270,9 @@ impl<'tcx> AutoTraitFinder<'tcx> {
polarity: ty::ImplPolarity::Positive,
}));
- let computed_preds = param_env.caller_bounds().iter();
- let mut user_computed_preds: FxIndexSet<_> = user_env.caller_bounds().iter().collect();
+ let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate());
+ let mut user_computed_preds: FxIndexSet<_> =
+ user_env.caller_bounds().iter().map(|c| c.as_predicate()).collect();
let mut new_env = param_env;
let dummy_cause = ObligationCause::dummy();
@@ -291,7 +292,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
new_env,
pred,
));
- let result = select.select(&obligation);
+ let result = select.poly_select(&obligation);
match result {
Ok(Some(ref impl_source)) => {
@@ -349,14 +350,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let normalized_preds =
elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned()));
new_env = ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(normalized_preds),
+ tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())),
param_env.reveal(),
param_env.constness(),
);
}
let final_user_env = ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(user_computed_preds.into_iter()),
+ tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())),
user_env.reveal(),
user_env.constness(),
);
@@ -400,8 +401,8 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let mut should_add_new = true;
user_computed_preds.retain(|&old_pred| {
if let (
- ty::PredicateKind::Clause(ty::Clause::Trait(new_trait)),
- ty::PredicateKind::Clause(ty::Clause::Trait(old_trait)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(new_trait)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(old_trait)),
) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder())
{
if new_trait.def_id() == old_trait.def_id() {
@@ -621,14 +622,14 @@ impl<'tcx> AutoTraitFinder<'tcx> {
let bound_predicate = predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => {
// Add this to `predicates` so that we end up calling `select`
// with it. If this predicate ends up being unimplemented,
// then `evaluate_predicates` will handle adding it the `ParamEnv`
// if possible.
predicates.push_back(bound_predicate.rebind(p));
}
- ty::PredicateKind::Clause(ty::Clause::Projection(p)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
let p = bound_predicate.rebind(p);
debug!(
"evaluate_nested_obligations: examining projection predicate {:?}",
@@ -758,11 +759,11 @@ impl<'tcx> AutoTraitFinder<'tcx> {
}
}
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(binder)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
selcx.infcx.region_outlives_predicate(&dummy_cause, binder)
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(binder)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => {
let binder = bound_predicate.rebind(binder);
match (
binder.no_bound_vars(),
@@ -793,7 +794,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
unevaluated,
Some(obligation.cause.span),
) {
- Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())),
+ Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())),
Ok(None) => {
let tcx = self.tcx;
let reported =
@@ -826,18 +827,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
// we start out with a `ParamEnv` with no inference variables,
// and these don't correspond to adding any new bounds to
// the `ParamEnv`.
- ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::Coerce(..) => {}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("predicate should only exist in the environment: {bound_predicate:?}")
- }
ty::PredicateKind::Ambiguous => return false,
};
}
diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
deleted file mode 100644
index 28967e1cc..000000000
--- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-//! Defines a Chalk-based `TraitEngine`
-
-use crate::infer::canonical::OriginalQueryValues;
-use crate::infer::InferCtxt;
-use crate::traits::query::NoSolution;
-use crate::traits::{
- ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
- SelectionError, TraitEngine,
-};
-use rustc_data_structures::fx::FxIndexSet;
-use rustc_middle::ty::TypeVisitableExt;
-
-pub struct FulfillmentContext<'tcx> {
- obligations: FxIndexSet<PredicateObligation<'tcx>>,
-
- usable_in_snapshot: bool,
-}
-
-impl FulfillmentContext<'_> {
- pub(super) fn new() -> Self {
- FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false }
- }
-
- pub(crate) fn new_in_snapshot() -> Self {
- FulfillmentContext { usable_in_snapshot: true, ..Self::new() }
- }
-}
-
-impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
- fn register_predicate_obligation(
- &mut self,
- infcx: &InferCtxt<'tcx>,
- obligation: PredicateObligation<'tcx>,
- ) {
- if !self.usable_in_snapshot {
- assert!(!infcx.is_in_snapshot());
- }
- let obligation = infcx.resolve_vars_if_possible(obligation);
-
- self.obligations.insert(obligation);
- }
-
- fn collect_remaining_errors(
- &mut self,
- _infcx: &InferCtxt<'tcx>,
- ) -> Vec<FulfillmentError<'tcx>> {
- // any remaining obligations are errors
- self.obligations
- .iter()
- .map(|obligation| FulfillmentError {
- obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeAmbiguity { overflow: false },
- // FIXME - does Chalk have a notation of 'root obligation'?
- // This is just for diagnostics, so it's okay if this is wrong
- root_obligation: obligation.clone(),
- })
- .collect()
- }
-
- fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
- if !self.usable_in_snapshot {
- assert!(!infcx.is_in_snapshot());
- }
-
- let mut errors = Vec::new();
- let mut next_round = FxIndexSet::default();
- let mut making_progress;
-
- loop {
- making_progress = false;
-
- // We iterate over all obligations, and record if we are able
- // to unambiguously prove at least one obligation.
- for obligation in self.obligations.drain(..) {
- let obligation = infcx.resolve_vars_if_possible(obligation);
- let environment = obligation.param_env.caller_bounds();
- let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
- let mut orig_values = OriginalQueryValues::default();
- if goal.references_error() {
- continue;
- }
-
- let canonical_goal =
- infcx.canonicalize_query_preserving_universes(goal, &mut orig_values);
-
- match infcx.tcx.evaluate_goal(canonical_goal) {
- Ok(response) => {
- if response.is_proven() {
- making_progress = true;
-
- match infcx.instantiate_query_response_and_region_obligations(
- &obligation.cause,
- obligation.param_env,
- &orig_values,
- &response,
- ) {
- Ok(infer_ok) => next_round.extend(
- infer_ok.obligations.into_iter().map(|obligation| {
- assert!(!infcx.is_in_snapshot());
- infcx.resolve_vars_if_possible(obligation)
- }),
- ),
-
- Err(_err) => errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- ),
- // FIXME - does Chalk have a notation of 'root obligation'?
- // This is just for diagnostics, so it's okay if this is wrong
- root_obligation: obligation,
- }),
- }
- } else {
- // Ambiguous: retry at next round.
- next_round.insert(obligation);
- }
- }
-
- Err(NoSolution) => errors.push(FulfillmentError {
- obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(
- SelectionError::Unimplemented,
- ),
- // FIXME - does Chalk have a notation of 'root obligation'?
- // This is just for diagnostics, so it's okay if this is wrong
- root_obligation: obligation,
- }),
- }
- }
- next_round = std::mem::replace(&mut self.obligations, next_round);
-
- if !making_progress {
- break;
- }
- }
-
- errors
- }
-
- fn drain_unstalled_obligations(
- &mut self,
- _: &InferCtxt<'tcx>,
- ) -> Vec<PredicateObligation<'tcx>> {
- unimplemented!()
- }
-
- fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
- self.obligations.iter().cloned().collect()
- }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index e8c5a8fab..1b1285e1b 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -5,7 +5,7 @@
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
use crate::infer::outlives::env::OutlivesEnvironment;
-use crate::infer::{CombinedSnapshot, InferOk};
+use crate::infer::InferOk;
use crate::traits::outlives_bounds::InferCtxtExt as _;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_subject_and_oblig;
@@ -23,13 +23,14 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
use rustc_span::symbol::sym;
use rustc_span::DUMMY_SP;
use std::fmt::Debug;
use std::iter;
use std::ops::ControlFlow;
+use super::query::evaluate_obligation::InferCtxtExt;
use super::NormalizeExt;
/// Whether we do the orphan check relative to this crate or
@@ -62,6 +63,21 @@ pub fn add_placeholder_note(err: &mut Diagnostic) {
);
}
+#[derive(Debug, Clone, Copy)]
+enum TrackAmbiguityCauses {
+ Yes,
+ No,
+}
+
+impl TrackAmbiguityCauses {
+ fn is_yes(self) -> bool {
+ match self {
+ TrackAmbiguityCauses::Yes => true,
+ TrackAmbiguityCauses::No => false,
+ }
+ }
+}
+
/// If there are types that satisfy both impls, returns `Some`
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, returns `None`.
@@ -97,29 +113,28 @@ pub fn overlapping_impls(
return None;
}
- let infcx = tcx
- .infer_ctxt()
- .with_opaque_type_inference(DefiningAnchor::Bubble)
- .intercrate(true)
- .build();
- let selcx = &mut SelectionContext::new(&infcx);
- let overlaps =
- overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
- if !overlaps {
- return None;
- }
+ let _overlap_with_bad_diagnostics = overlap(
+ tcx,
+ TrackAmbiguityCauses::No,
+ skip_leak_check,
+ impl1_def_id,
+ impl2_def_id,
+ overlap_mode,
+ )?;
// In the case where we detect an error, run the check again, but
// this time tracking intercrate ambiguity causes for better
// diagnostics. (These take time and can lead to false errors.)
- let infcx = tcx
- .infer_ctxt()
- .with_opaque_type_inference(DefiningAnchor::Bubble)
- .intercrate(true)
- .build();
- let selcx = &mut SelectionContext::new(&infcx);
- selcx.enable_tracking_intercrate_ambiguity_causes();
- Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
+ let overlap = overlap(
+ tcx,
+ TrackAmbiguityCauses::Yes,
+ skip_leak_check,
+ impl1_def_id,
+ impl2_def_id,
+ overlap_mode,
+ )
+ .unwrap();
+ Some(overlap)
}
fn with_fresh_ty_vars<'cx, 'tcx>(
@@ -134,7 +149,12 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
impl_def_id,
self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs),
trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
- predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
+ predicates: tcx
+ .predicates_of(impl_def_id)
+ .instantiate(tcx, impl_substs)
+ .iter()
+ .map(|(c, _)| c.as_predicate())
+ .collect(),
};
let InferOk { value: mut header, obligations } =
@@ -146,40 +166,35 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
/// Can both impl `a` and impl `b` be satisfied by a common type (including
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
-fn overlap<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
+#[instrument(level = "debug", skip(tcx))]
+fn overlap<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ track_ambiguity_causes: TrackAmbiguityCauses,
skip_leak_check: SkipLeakCheck,
impl1_def_id: DefId,
impl2_def_id: DefId,
overlap_mode: OverlapMode,
) -> Option<OverlapResult<'tcx>> {
- debug!(
- "overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})",
- impl1_def_id, impl2_def_id, overlap_mode
- );
-
- selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
- overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot)
- })
-}
-
-fn overlap_within_probe<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
- impl1_def_id: DefId,
- impl2_def_id: DefId,
- overlap_mode: OverlapMode,
- snapshot: &CombinedSnapshot<'tcx>,
-) -> Option<OverlapResult<'tcx>> {
- let infcx = selcx.infcx;
-
if overlap_mode.use_negative_impl() {
- if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id)
- || negative_impl(infcx.tcx, impl2_def_id, impl1_def_id)
+ if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id)
+ || impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id)
{
return None;
}
}
+ let infcx = tcx
+ .infer_ctxt()
+ .with_opaque_type_inference(DefiningAnchor::Bubble)
+ .skip_leak_check(skip_leak_check.is_yes())
+ .intercrate(true)
+ .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
+ .build();
+ let selcx = &mut SelectionContext::new(&infcx);
+ if track_ambiguity_causes.is_yes() {
+ selcx.enable_tracking_intercrate_ambiguity_causes();
+ }
+
// For the purposes of this check, we don't bring any placeholder
// types into scope; instead, we replace the generic types with
// fresh type variables, and hence we do our evaluations in an
@@ -189,27 +204,40 @@ fn overlap_within_probe<'cx, 'tcx>(
let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id);
let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id);
- let obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
+ // Equate the headers to find their intersection (the general type, with infer vars,
+ // that may apply both impls).
+ let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?;
debug!("overlap: unification check succeeded");
- if overlap_mode.use_implicit_negative() {
- if implicit_negative(selcx, param_env, &impl1_header, impl2_header, obligations) {
- return None;
- }
+ if overlap_mode.use_implicit_negative()
+ && impl_intersection_has_impossible_obligation(
+ selcx,
+ param_env,
+ &impl1_header,
+ impl2_header,
+ equate_obligations,
+ )
+ {
+ return None;
}
- // We disable the leak when creating the `snapshot` by using
- // `infcx.probe_maybe_disable_leak_check`.
- if infcx.leak_check(true, snapshot).is_err() {
+ // We toggle the `leak_check` by using `skip_leak_check` when constructing the
+ // inference context, so this may be a noop.
+ if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
debug!("overlap: leak check failed");
return None;
}
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
-
- let involves_placeholder =
- matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true));
+ let involves_placeholder = infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .data()
+ .constraints
+ .iter()
+ .any(|c| c.0.involves_placeholders());
let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
@@ -236,55 +264,55 @@ fn equate_impl_headers<'tcx>(
result.map(|infer_ok| infer_ok.obligations).ok()
}
-/// Given impl1 and impl2 check if both impls can be satisfied by a common type (including
-/// where-clauses) If so, return false, otherwise return true, they are disjoint.
-fn implicit_negative<'cx, 'tcx>(
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of either impl's obligations is not known to hold.
+///
+/// For example, given these two impls:
+/// `impl From<MyLocalType> for Box<dyn Error>` (in my crate)
+/// `impl<E> From<E> for Box<dyn Error> where E: Error` (in libstd)
+///
+/// After replacing both impl headers with inference vars (which happens before
+/// this function is called), we get:
+/// `Box<dyn Error>: From<MyLocalType>`
+/// `Box<dyn Error>: From<?E>`
+///
+/// This gives us `?E = MyLocalType`. We then certainly know that `MyLocalType: Error`
+/// never holds in intercrate mode since a local impl does not exist, and a
+/// downstream impl cannot be added -- therefore can consider the intersection
+/// of the two impls above to be empty.
+///
+/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
+fn impl_intersection_has_impossible_obligation<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
impl1_header: &ty::ImplHeader<'tcx>,
impl2_header: ty::ImplHeader<'tcx>,
obligations: PredicateObligations<'tcx>,
) -> bool {
- // There's no overlap if obligations are unsatisfiable or if the obligation negated is
- // satisfied.
- //
- // For example, given these two impl headers:
- //
- // `impl<'a> From<&'a str> for Box<dyn Error>`
- // `impl<E> From<E> for Box<dyn Error> where E: Error`
- //
- // So we have:
- //
- // `Box<dyn Error>: From<&'?a str>`
- // `Box<dyn Error>: From<?E>`
- //
- // After equating the two headers:
- //
- // `Box<dyn Error> = Box<dyn Error>`
- // So, `?E = &'?a str` and then given the where clause `&'?a str: Error`.
- //
- // If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't
- // hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because
- // at some point an impl for `&'?a str: Error` could be added.
- debug!(
- "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})",
- impl1_header, impl2_header, obligations
- );
let infcx = selcx.infcx;
- let opt_failing_obligation = impl1_header
- .predicates
- .iter()
- .copied()
- .chain(impl2_header.predicates)
- .map(|p| infcx.resolve_vars_if_possible(p))
- .map(|p| Obligation {
- cause: ObligationCause::dummy(),
- param_env,
- recursion_depth: 0,
- predicate: p,
+
+ let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| {
+ if infcx.next_trait_solver() {
+ infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply())
+ } else {
+ // We use `evaluate_root_obligation` to correctly track
+ // intercrate ambiguity clauses. We do not need this in the
+ // new solver.
+ selcx.evaluate_root_obligation(obligation).map_or(
+ false, // Overflow has occurred, and treat the obligation as possibly holding.
+ |result| !result.may_apply(),
+ )
+ }
+ };
+
+ let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates]
+ .into_iter()
+ .flatten()
+ .map(|&predicate| {
+ Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate)
})
.chain(obligations)
- .find(|o| !selcx.predicate_may_hold_fatal(o));
+ .find(obligation_guaranteed_to_fail);
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
@@ -294,9 +322,27 @@ fn implicit_negative<'cx, 'tcx>(
}
}
-/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
-/// where-clauses) If so, return true, they are disjoint and false otherwise.
-fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
+/// Check if both impls can be satisfied by a common type by considering whether
+/// any of first impl's obligations is known not to hold *via a negative predicate*.
+///
+/// For example, given these two impls:
+/// `struct MyCustomBox<T: ?Sized>(Box<T>);`
+/// `impl From<&str> for MyCustomBox<dyn Error>` (in my crate)
+/// `impl<E> From<E> for MyCustomBox<dyn Error> where E: Error` (in my crate)
+///
+/// After replacing the second impl's header with inference vars, we get:
+/// `MyCustomBox<dyn Error>: From<&str>`
+/// `MyCustomBox<dyn Error>: From<?E>`
+///
+/// This gives us `?E = &str`. We then try to prove the first impl's predicates
+/// after negating, giving us `&str: !Error`. This is a negative impl provided by
+/// libstd, and therefore we can guarantee for certain that libstd will never add
+/// a positive impl for `&str: Error` (without it being a breaking change).
+fn impl_intersection_has_negative_obligation(
+ tcx: TyCtxt<'_>,
+ impl1_def_id: DefId,
+ impl2_def_id: DefId,
+) -> bool {
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
// Create an infcx, taking the predicates of impl1 as assumptions:
@@ -322,57 +368,45 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b
// Attempt to prove that impl2 applies, given all of the above.
let selcx = &mut SelectionContext::new(&infcx);
let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
- let (subject2, obligations) =
+ let (subject2, normalization_obligations) =
impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| {
ObligationCause::dummy()
});
- !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
-}
-
-fn equate<'tcx>(
- infcx: &InferCtxt<'tcx>,
- impl_env: ty::ParamEnv<'tcx>,
- subject1: ImplSubject<'tcx>,
- subject2: ImplSubject<'tcx>,
- obligations: impl Iterator<Item = PredicateObligation<'tcx>>,
- body_def_id: DefId,
-) -> bool {
- // do the impls unify? If not, not disjoint.
- let Ok(InferOk { obligations: more_obligations, .. }) =
+ // do the impls unify? If not, then it's not currently possible to prove any
+ // obligations about their intersection.
+ let Ok(InferOk { obligations: equate_obligations, .. }) =
infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2)
else {
debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2);
- return true;
+ return false;
};
- let opt_failing_obligation = obligations
- .into_iter()
- .chain(more_obligations)
- .find(|o| negative_impl_exists(infcx, o, body_def_id));
-
- if let Some(failing_obligation) = opt_failing_obligation {
- debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
- false
- } else {
- true
+ for obligation in normalization_obligations.into_iter().chain(equate_obligations) {
+ if negative_impl_exists(&infcx, &obligation, impl1_def_id) {
+ debug!("overlap: obligation unsatisfiable {:?}", obligation);
+ return true;
+ }
}
+
+ false
}
-/// Try to prove that a negative impl exist for the given obligation and its super predicates.
+/// Try to prove that a negative impl exist for the obligation or its supertraits.
+///
+/// If such a negative impl exists, then the obligation definitely must not hold
+/// due to coherence, even if it's not necessarily "knowable" in this crate. Any
+/// valid impl downstream would not be able to exist due to the overlapping
+/// negative impl.
#[instrument(level = "debug", skip(infcx))]
fn negative_impl_exists<'tcx>(
infcx: &InferCtxt<'tcx>,
o: &PredicateObligation<'tcx>,
body_def_id: DefId,
) -> bool {
- if resolve_negative_obligation(infcx.fork(), o, body_def_id) {
- return true;
- }
-
// Try to prove a negative obligation exists for super predicates
for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) {
- if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
+ if prove_negated_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) {
return true;
}
}
@@ -381,7 +415,7 @@ fn negative_impl_exists<'tcx>(
}
#[instrument(level = "debug", skip(infcx))]
-fn resolve_negative_obligation<'tcx>(
+fn prove_negated_obligation<'tcx>(
infcx: InferCtxt<'tcx>,
o: &PredicateObligation<'tcx>,
body_def_id: DefId,
@@ -403,7 +437,11 @@ fn resolve_negative_obligation<'tcx>(
let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
let ocx = ObligationCtxt::new(&infcx);
- let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id);
+ let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id)
+ else {
+ return false;
+ };
+
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, body_def_id, wf_tys),
@@ -676,7 +714,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
| ty::RawPtr(..)
| ty::Never
| ty::Tuple(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty),
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => {
+ self.found_non_local_ty(ty)
+ }
ty::Param(..) => self.found_param_ty(ty),
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index bd1ea43a7..8dc13b827 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -176,7 +176,7 @@ fn satisfied_from_param_env<'tcx>(
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("is_const_evaluatable: candidate={:?}", c);
if self.infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
+ let ocx = ObligationCtxt::new(self.infcx);
ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok()
&& ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok()
&& ocx.select_all_or_error().is_empty()
@@ -207,7 +207,7 @@ fn satisfied_from_param_env<'tcx>(
for pred in param_env.caller_bounds() {
match pred.kind().skip_binder() {
- ty::PredicateKind::ConstEvaluatable(ce) => {
+ ty::ClauseKind::ConstEvaluatable(ce) => {
let b_ct = tcx.expand_abstract_consts(ce);
let mut v = Visitor { ct, infcx, param_env, single_match };
let _ = b_ct.visit_with(&mut v);
@@ -219,7 +219,7 @@ fn satisfied_from_param_env<'tcx>(
}
if let Some(Ok(c)) = single_match {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok());
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
assert!(ocx.select_all_or_error().is_empty());
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 2c5ffd664..61f693e1b 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -1,9 +1,10 @@
use std::cell::RefCell;
use std::fmt::Debug;
+use super::FulfillmentContext;
use super::TraitEngine;
-use super::{ChalkFulfillmentContext, FulfillmentContext};
use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
+use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::NormalizeExt;
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::ErrorGuaranteed;
@@ -24,27 +25,25 @@ use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::config::TraitSolver;
-use rustc_span::Span;
pub trait TraitEngineExt<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
- fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
+ fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
}
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Box<Self> {
- match tcx.sess.opts.unstable_opts.trait_solver {
- TraitSolver::Classic => Box::new(FulfillmentContext::new()),
- TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()),
- TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
- }
- }
-
- fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
- match tcx.sess.opts.unstable_opts.trait_solver {
- TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()),
- TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()),
- TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
+ fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
+ match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
+ (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
+ Box::new(FulfillmentContext::new(infcx))
+ }
+ (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
+ Box::new(NextFulfillmentCtxt::new(infcx))
+ }
+ _ => bug!(
+ "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
+ infcx.tcx.sess.opts.unstable_opts.trait_solver,
+ infcx.next_trait_solver()
+ ),
}
}
}
@@ -58,11 +57,7 @@ pub struct ObligationCtxt<'a, 'tcx> {
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
- Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
- }
-
- pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self {
- Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
+ Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
}
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
@@ -202,17 +197,24 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
}
}
+ pub fn assumed_wf_types_and_report_errors(
+ &self,
+ param_env: ty::ParamEnv<'tcx>,
+ def_id: LocalDefId,
+ ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> {
+ self.assumed_wf_types(param_env, def_id)
+ .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(&errors))
+ }
+
pub fn assumed_wf_types(
&self,
param_env: ty::ParamEnv<'tcx>,
- span: Span,
def_id: LocalDefId,
- ) -> FxIndexSet<Ty<'tcx>> {
+ ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> {
let tcx = self.infcx.tcx;
- let assumed_wf_types = tcx.assumed_wf_types(def_id);
let mut implied_bounds = FxIndexSet::default();
- let cause = ObligationCause::misc(span, def_id);
- for ty in assumed_wf_types {
+ let mut errors = Vec::new();
+ for &(ty, span) in tcx.assumed_wf_types(def_id) {
// FIXME(@lcnr): rustc currently does not check wf for types
// pre-normalization, meaning that implied bounds are sometimes
// incorrect. See #100910 for more details.
@@ -225,10 +227,19 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
// sound and then uncomment this line again.
// implied_bounds.insert(ty);
- let normalized = self.normalize(&cause, param_env, ty);
- implied_bounds.insert(normalized);
+ let cause = ObligationCause::misc(span, def_id);
+ match self
+ .infcx
+ .at(&cause, param_env)
+ .deeply_normalize(ty, &mut **self.engine.borrow_mut())
+ {
+ // Insert well-formed types, ignoring duplicates.
+ Ok(normalized) => drop(implied_bounds.insert(normalized)),
+ Err(normalization_errors) => errors.extend(normalization_errors),
+ };
}
- implied_bounds
+
+ if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
}
pub fn make_canonicalized_query_response<T>(
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index 7ab652761..f785c4eaf 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -1,7 +1,7 @@
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
use rustc_infer::traits::util::elaborate;
-use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
+use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
use rustc_middle::ty;
use rustc_span::{Span, DUMMY_SP};
@@ -14,13 +14,13 @@ pub enum Ambiguity {
pub fn recompute_applicable_impls<'tcx>(
infcx: &InferCtxt<'tcx>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Vec<Ambiguity> {
let tcx = infcx.tcx;
let param_env = obligation.param_env;
let impl_may_apply = |impl_def_id| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
@@ -45,7 +45,7 @@ pub fn recompute_applicable_impls<'tcx>(
};
let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let placeholder_obligation =
infcx.instantiate_binder_with_placeholders(obligation.predicate);
let obligation_trait_ref =
@@ -84,7 +84,7 @@ pub fn recompute_applicable_impls<'tcx>(
tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
for (pred, span) in elaborate(tcx, predicates.into_iter()) {
let kind = pred.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
+ if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
{
if kind.rebind(trait_pred.trait_ref) == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) {
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 a10ececbb..f34218059 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -5,13 +5,13 @@ pub mod suggestions;
use super::{
FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause,
ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow,
- PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
+ PredicateObligation, SelectionError, TraitNotObjectSafe,
};
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{self, InferCtxt};
+use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::query::normalize::QueryNormalizeExt as _;
use crate::traits::specialize::to_pretty_impl_header;
use crate::traits::NormalizeExt;
use on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
@@ -28,21 +28,24 @@ use rustc_hir::{GenericParam, Item, Node};
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_middle::traits::select::OverflowError;
-use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
+use rustc_middle::traits::solve::Goal;
+use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch};
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
TypeVisitable, TypeVisitableExt,
};
-use rustc_session::config::TraitSolver;
+use rustc_session::config::{DumpSolverProofTree, TraitSolver};
use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::sym;
use rustc_span::{ExpnKind, Span, DUMMY_SP};
+use std::borrow::Cow;
use std::fmt;
+use std::io::Write;
use std::iter;
use std::ops::ControlFlow;
use suggestions::TypeErrCtxtExt as _;
@@ -59,12 +62,17 @@ pub enum CandidateSimilarity {
Fuzzy { ignoring_lifetimes: bool },
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ImplCandidate<'tcx> {
pub trait_ref: ty::TraitRef<'tcx>,
pub similarity: CandidateSimilarity,
}
+enum GetSafeTransmuteErrorAndReason {
+ Silent,
+ Error { err_msg: String, safe_transmute_explanation: String },
+}
+
pub trait InferCtxtExt<'tcx> {
/// Given some node representing a fn-like thing in the HIR map,
/// returns a span and `ArgKind` information that describes the
@@ -148,6 +156,12 @@ pub trait TypeErrCtxtExt<'tcx> {
root_obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>,
);
+
+ fn report_const_param_not_wf(
+ &self,
+ ty: Ty<'tcx>,
+ obligation: &PredicateObligation<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
@@ -365,7 +379,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
param_env,
ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }),
);
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
ocx.register_obligation(obligation);
if ocx.select_all_or_error().is_empty() {
return Ok((
@@ -618,6 +632,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
error: &SelectionError<'tcx>,
) {
let tcx = self.tcx;
+
+ if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+ dump_proof_tree(root_obligation, self.infcx);
+ }
+
let mut span = obligation.cause.span;
// FIXME: statically guarantee this by tainting after the diagnostic is emitted
self.set_tainted_by_errors(
@@ -640,6 +659,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span = obligation.cause.span;
}
}
+
if let ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id,
trait_item_def_id,
@@ -656,9 +676,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
+ // Report a const-param specific error
+ if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives()
+ {
+ self.report_const_param_not_wf(ty, &obligation).emit();
+ return;
+ }
+
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
let trait_predicate = bound_predicate.rebind(trait_predicate);
let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate);
@@ -724,11 +751,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
== self.tcx.lang_items().transmute_trait()
{
// Recompute the safe transmute reason and use that for the error reporting
- self.get_safe_transmute_error_and_reason(
+ match self.get_safe_transmute_error_and_reason(
obligation.clone(),
trait_ref,
span,
- )
+ ) {
+ GetSafeTransmuteErrorAndReason::Silent => return,
+ GetSafeTransmuteErrorAndReason::Error {
+ err_msg,
+ safe_transmute_explanation,
+ } => (err_msg, Some(safe_transmute_explanation)),
+ }
} else {
(err_msg, None)
};
@@ -940,7 +973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& self.fallback_has_occurred
{
let predicate = trait_predicate.map_bound(|trait_pred| {
- trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit())
+ trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx))
});
let unit_obligation = obligation.with(tcx, predicate);
if self.predicate_may_hold(&unit_obligation) {
@@ -995,8 +1028,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => {
span_bug!(
span,
"outlives clauses should not error outside borrowck. obligation: `{:?}`",
@@ -1004,7 +1037,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
span_bug!(
span,
"projection clauses should be implied from elsewhere. obligation: `{:?}`",
@@ -1022,7 +1055,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.report_closure_error(&obligation, closure_def_id, found_kind, kind)
}
- ty::PredicateKind::WellFormed(ty) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
+ let ty = self.resolve_vars_if_possible(ty);
match self.tcx.sess.opts.unstable_opts.trait_solver {
TraitSolver::Classic => {
// WF predicates cannot themselves make
@@ -1032,7 +1066,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// (which may fail).
span_bug!(span, "WF predicate not satisfied for {:?}", ty);
}
- TraitSolver::Chalk | TraitSolver::Next => {
+ TraitSolver::Next | TraitSolver::NextCoherence => {
// FIXME: we'll need a better message which takes into account
// which bounds actually failed to hold.
self.tcx.sess.struct_span_err(
@@ -1043,7 +1077,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(..) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => {
// Errors for `ConstEvaluatable` predicates show up as
// `SelectionError::ConstEvalFailure`,
// not `Unimplemented`.
@@ -1067,17 +1101,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"),
- ty::PredicateKind::TypeWellFormedFromEnv(..) => span_bug!(
- span,
- "TypeWellFormedFromEnv predicate should only exist in the environment"
- ),
-
ty::PredicateKind::AliasRelate(..) => span_bug!(
span,
"AliasRelate predicate should never be the predicate cause of a SelectionError"
),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
let mut diag = self.tcx.sess.struct_span_err(
span,
format!("the constant `{}` is not of type `{}`", ct, ty),
@@ -1122,6 +1151,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
+ SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage(
+ &obligation,
+ def_id,
+ ),
+
TraitNotObjectSafe(did) => {
let violations = self.tcx.object_safety_violations(did);
report_object_safety_error(self.tcx, span, did, violations)
@@ -1140,16 +1174,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// Already reported in the query.
- SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => {
- // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token.
- self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error");
- return;
- }
+ SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) |
// Already reported.
- Overflow(OverflowError::Error(_)) => {
- self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported");
- return;
- }
+ Overflow(OverflowError::Error(_)) => return,
+
Overflow(_) => {
bug!("overflow should be handled before the `report_selection_error` path");
}
@@ -1162,6 +1190,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit();
}
+
+ fn report_const_param_not_wf(
+ &self,
+ ty: Ty<'tcx>,
+ obligation: &PredicateObligation<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let span = obligation.cause.span;
+
+ let mut diag = match ty.kind() {
+ _ if ty.has_param() => {
+ span_bug!(span, "const param tys cannot mention other generic parameters");
+ }
+ ty::Float(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` is forbidden as the type of a const generic parameter",
+ )
+ }
+ ty::FnPtr(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "using function pointers as const generic parameters is forbidden",
+ )
+ }
+ ty::RawPtr(_) => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "using raw pointers as const generic parameters is forbidden",
+ )
+ }
+ ty::Adt(def, _) => {
+ // We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
+ let mut diag = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter",
+ );
+ // Only suggest derive if this isn't a derived obligation,
+ // and the struct is local.
+ if let Some(span) = self.tcx.hir().span_if_local(def.did())
+ && obligation.cause.code().parent().is_none()
+ {
+ if ty.is_structural_eq_shallow(self.tcx) {
+ diag.span_suggestion(
+ span,
+ "add `#[derive(ConstParamTy)]` to the struct",
+ "#[derive(ConstParamTy)]\n",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ // FIXME(adt_const_params): We should check there's not already an
+ // overlapping `Eq`/`PartialEq` impl.
+ diag.span_suggestion(
+ span,
+ "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct",
+ "#[derive(ConstParamTy, PartialEq, Eq)]\n",
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ diag
+ }
+ _ => {
+ struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0741,
+ "`{ty}` can't be used as a const parameter type",
+ )
+ }
+ };
+
+ let mut code = obligation.cause.code();
+ let mut pred = obligation.predicate.to_opt_poly_trait_pred();
+ while let Some((next_code, next_pred)) = code.parent() {
+ if let Some(pred) = pred {
+ let pred = self.instantiate_binder_with_placeholders(pred);
+ diag.note(format!(
+ "`{}` must implement `{}`, but it does not",
+ pred.self_ty(),
+ pred.print_modifiers_and_trait_path()
+ ));
+ }
+ code = next_code;
+ pred = next_pred;
+ }
+
+ diag
+ }
}
trait InferCtxtPrivExt<'tcx> {
@@ -1292,7 +1416,7 @@ trait InferCtxtPrivExt<'tcx> {
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
- ) -> (String, Option<String>);
+ ) -> GetSafeTransmuteErrorAndReason;
fn add_tuple_trait_message(
&self,
@@ -1345,6 +1469,12 @@ trait InferCtxtPrivExt<'tcx> {
terr: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+ fn report_opaque_type_auto_trait_leakage(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ def_id: DefId,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
+
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -1372,8 +1502,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let bound_error = error.kind();
let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) {
(
- ty::PredicateKind::Clause(ty::Clause::Trait(..)),
- ty::PredicateKind::Clause(ty::Clause::Trait(error)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)),
) => (cond, bound_error.rebind(error)),
_ => {
// FIXME: make this work in other cases too.
@@ -1383,7 +1513,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for pred in super::elaborate(self.tcx, std::iter::once(cond)) {
let bound_predicate = pred.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) =
bound_predicate.skip_binder()
{
let error = error.to_poly_trait_ref();
@@ -1404,6 +1534,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) {
+ if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError {
+ dump_proof_tree(&error.root_obligation, self.infcx);
+ }
+
match error.code {
FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
self.report_selection_error(
@@ -1474,14 +1608,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
self.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(self);
+ let ocx = ObligationCtxt::new(self);
// try to find the mismatched types to report the error with.
//
// this can fail if the problem was higher-ranked, in which
// cause I have no idea for a good error message.
let bound_predicate = predicate.kind();
- let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
+ let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) =
bound_predicate.skip_binder()
{
let data = self.instantiate_binder_with_fresh_vars(
@@ -1490,20 +1624,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
bound_predicate.rebind(data),
);
let unnormalized_term = match data.term.unpack() {
- ty::TermKind::Ty(_) => self
- .tcx
- .mk_projection(data.projection_ty.def_id, data.projection_ty.substs)
- .into(),
- ty::TermKind::Const(ct) => self
- .tcx
- .mk_const(
- ty::UnevaluatedConst {
- def: data.projection_ty.def_id,
- substs: data.projection_ty.substs,
- },
- ct.ty(),
- )
- .into(),
+ ty::TermKind::Ty(_) => Ty::new_projection(
+ self.tcx,
+ data.projection_ty.def_id,
+ data.projection_ty.substs,
+ )
+ .into(),
+ ty::TermKind::Const(ct) => ty::Const::new_unevaluated(
+ self.tcx,
+ ty::UnevaluatedConst {
+ def: data.projection_ty.def_id,
+ substs: data.projection_ty.substs,
+ },
+ ct.ty(),
+ )
+ .into(),
};
let normalized_term =
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
@@ -1564,7 +1699,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
let secondary_span = (|| {
- let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
+ let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
predicate.kind().skip_binder()
else {
return None;
@@ -1602,7 +1737,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}),
) => Some((
ty.span,
- with_forced_trimmed_paths!(format!(
+ with_forced_trimmed_paths!(Cow::from(format!(
"type mismatch resolving `{}`",
self.resolve_vars_if_possible(predicate)
.print(FmtPrinter::new_with_limit(
@@ -1612,7 +1747,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
))
.unwrap()
.into_buffer()
- )),
+ ))),
)),
_ => None,
}
@@ -1702,12 +1837,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::Alias(ty::Projection, ..) => Some(12),
ty::Alias(ty::Inherent, ..) => Some(13),
ty::Alias(ty::Opaque, ..) => Some(14),
- ty::Never => Some(15),
- ty::Adt(..) => Some(16),
- ty::Generator(..) => Some(17),
- ty::Foreign(..) => Some(18),
- ty::GeneratorWitness(..) => Some(19),
- ty::GeneratorWitnessMIR(..) => Some(20),
+ ty::Alias(ty::Weak, ..) => Some(15),
+ ty::Never => Some(16),
+ ty::Adt(..) => Some(17),
+ ty::Generator(..) => Some(18),
+ ty::Foreign(..) => Some(19),
+ ty::GeneratorWitness(..) => Some(20),
+ ty::GeneratorWitnessMIR(..) => Some(21),
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
}
}
@@ -1775,6 +1911,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|| !trait_pred
.skip_binder()
.is_constness_satisfied_by(self.tcx.constness(def_id))
+ || !self.tcx.is_user_visible_dep(def_id.krate)
{
return None;
}
@@ -1803,10 +1940,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
other: bool,
) -> bool {
let other = if other { "other " } else { "" };
- let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
- candidates.sort();
- candidates.dedup();
- let len = candidates.len();
+ let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
if candidates.is_empty() {
return false;
}
@@ -1835,11 +1969,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect();
traits.sort();
traits.dedup();
+ // FIXME: this could use a better heuristic, like just checking
+ // that substs[1..] is the same.
+ let all_traits_equal = traits.len() == 1;
- let mut candidates: Vec<String> = candidates
+ let candidates: Vec<String> = candidates
.into_iter()
.map(|c| {
- if traits.len() == 1 {
+ if all_traits_equal {
format!("\n {}", c.self_ty())
} else {
format!("\n {}", c)
@@ -1847,14 +1984,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
})
.collect();
- candidates.sort();
- candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
err.help(format!(
"the following {other}types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
- if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
+ if candidates.len() > 9 {
+ format!("\nand {} others", candidates.len() - 8)
+ } else {
+ String::new()
+ }
));
true
};
@@ -1868,7 +2007,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Mentioning implementers of `Copy`, `Debug` and friends is not useful.
return false;
}
- let normalized_impl_candidates: Vec<_> = self
+ let mut impl_candidates: Vec<_> = self
.tcx
.all_impls(def_id)
// Ignore automatically derived impls and `!Trait` impls.
@@ -1895,7 +2034,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
})
.collect();
- return report(normalized_impl_candidates, err);
+
+ impl_candidates.sort();
+ impl_candidates.dedup();
+ return report(impl_candidates, err);
}
// Sort impl candidates so that ordering is consistent for UI tests.
@@ -1904,27 +2046,25 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
//
// Prefer more similar candidates first, then sort lexicographically
// by their normalized string representation.
- let mut normalized_impl_candidates_and_similarities = impl_candidates
+ let mut impl_candidates: Vec<_> = impl_candidates
.iter()
- .copied()
- .map(|ImplCandidate { trait_ref, similarity }| {
- // FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
- let normalized = self
- .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
- .query_normalize(trait_ref)
- .map_or(trait_ref, |normalized| normalized.value);
- (similarity, normalized)
+ .cloned()
+ .map(|mut cand| {
+ // Fold the consts so that they shows up as, e.g., `10`
+ // instead of `core::::array::{impl#30}::{constant#0}`.
+ cand.trait_ref = cand.trait_ref.fold_with(&mut BottomUpFolder {
+ tcx: self.tcx,
+ ty_op: |ty| ty,
+ lt_op: |lt| lt,
+ ct_op: |ct| ct.eval(self.tcx, ty::ParamEnv::empty()),
+ });
+ cand
})
- .collect::<Vec<_>>();
- normalized_impl_candidates_and_similarities.sort();
- normalized_impl_candidates_and_similarities.dedup();
-
- let normalized_impl_candidates = normalized_impl_candidates_and_similarities
- .into_iter()
- .map(|(_, normalized)| normalized)
- .collect::<Vec<_>>();
+ .collect();
+ impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref));
+ impl_candidates.dedup();
- report(normalized_impl_candidates, err)
+ report(impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(), err)
}
fn report_similar_impl_candidates_for_root_obligation(
@@ -2075,7 +2215,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let bound_predicate = predicate.kind();
let mut err = match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_ref = bound_predicate.rebind(data.trait_ref);
debug!(?trait_ref);
@@ -2145,55 +2285,40 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
};
- let obligation = obligation.with(self.tcx, trait_ref);
- let mut selcx = SelectionContext::new(&self);
- match selcx.select_from_obligation(&obligation) {
- Ok(None) => {
- let ambiguities =
- ambiguity::recompute_applicable_impls(self.infcx, &obligation);
- let has_non_region_infer = trait_ref
- .skip_binder()
- .substs
- .types()
- .any(|t| !t.is_ty_or_numeric_infer());
- // It doesn't make sense to talk about applicable impls if there are more
- // than a handful of them.
- if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
- if self.tainted_by_errors().is_some() && subst.is_none() {
- // If `subst.is_none()`, then this is probably two param-env
- // candidates or impl candidates that are equal modulo lifetimes.
- // Therefore, if we've already emitted an error, just skip this
- // one, since it's not particularly actionable.
- err.cancel();
- return;
- }
- self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
- } else {
- if self.tainted_by_errors().is_some() {
- err.cancel();
- return;
- }
- err.note(format!("cannot satisfy `{}`", predicate));
- let impl_candidates = self.find_similar_impl_candidates(
- predicate.to_opt_poly_trait_pred().unwrap(),
- );
- if impl_candidates.len() < 10 {
- self.report_similar_impl_candidates(
- impl_candidates.as_slice(),
- trait_ref,
- obligation.cause.body_id,
- &mut err,
- false,
- );
- }
- }
+ let ambiguities = ambiguity::recompute_applicable_impls(
+ self.infcx,
+ &obligation.with(self.tcx, trait_ref),
+ );
+ let has_non_region_infer =
+ trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_or_numeric_infer());
+ // It doesn't make sense to talk about applicable impls if there are more
+ // than a handful of them.
+ if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
+ if self.tainted_by_errors().is_some() && subst.is_none() {
+ // If `subst.is_none()`, then this is probably two param-env
+ // candidates or impl candidates that are equal modulo lifetimes.
+ // Therefore, if we've already emitted an error, just skip this
+ // one, since it's not particularly actionable.
+ err.cancel();
+ return;
}
- _ => {
- if self.tainted_by_errors().is_some() {
- err.cancel();
- return;
- }
- err.note(format!("cannot satisfy `{}`", predicate));
+ self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
+ } else {
+ if self.tainted_by_errors().is_some() {
+ err.cancel();
+ return;
+ }
+ err.note(format!("cannot satisfy `{}`", predicate));
+ let impl_candidates = self
+ .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap());
+ if impl_candidates.len() < 10 {
+ self.report_similar_impl_candidates(
+ impl_candidates.as_slice(),
+ trait_ref,
+ obligation.cause.body_id,
+ &mut err,
+ false,
+ );
}
}
@@ -2258,17 +2383,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next()
{
let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count();
- let message = if non_blanket_impl_count == 1 {
- "use the fully-qualified path to the only available implementation".to_string()
- } else {
+ // If there is only one implementation of the trait, suggest using it.
+ // Otherwise, use a placeholder comment for the implementation.
+ let (message, impl_suggestion) = if non_blanket_impl_count == 1 {(
+ "use the fully-qualified path to the only available implementation".to_string(),
+ format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity())
+ )} else {(
format!(
"use a fully-qualified path to a specific available implementation ({} found)",
non_blanket_impl_count
- )
- };
+ ),
+ "</* self type */ as ".to_string()
+ )};
let mut suggestions = vec![(
path.span.shrink_to_lo(),
- format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity())
+ impl_suggestion
)];
if let Some(generic_arg) = trait_path_segment.args {
let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext);
@@ -2291,7 +2420,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// Same hacky approach as above to avoid deluging user
// with error messages.
if arg.references_error()
@@ -2329,7 +2458,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
true,
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
if predicate.references_error() || self.tainted_by_errors().is_some() {
return;
}
@@ -2363,7 +2492,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(data) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
if predicate.references_error() || self.tainted_by_errors().is_some() {
return;
}
@@ -2526,11 +2655,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() {
+ if let ty::Param(_) = *ty.kind() {
let infcx = self.infcx;
*self.var_map.entry(ty).or_insert_with(|| {
infcx.next_ty_var(TypeVariableOrigin {
- kind: TypeVariableOriginKind::TypeParameterDefinition(name, None),
+ kind: TypeVariableOriginKind::MiscVariable,
span: DUMMY_SP,
})
})
@@ -2577,7 +2706,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
obligation: &PredicateObligation<'tcx>,
) {
- let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; };
+ let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; };
let (ObligationCauseCode::BindingObligation(item_def_id, span)
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..))
= *obligation.cause.code().peel_derives() else { return; };
@@ -2738,7 +2867,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
obligation: PredicateObligation<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
span: Span,
- ) -> (String, Option<String>) {
+ ) -> GetSafeTransmuteErrorAndReason {
+ use rustc_transmute::Answer;
+
// Erase regions because layout code doesn't particularly care about regions.
let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref));
@@ -2751,19 +2882,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.substs.const_at(3)) else {
span_bug!(span, "Unable to construct rustc_transmute::Assume where it was previously possible");
};
+
match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
obligation.cause,
src_and_dst,
scope,
assume,
) {
- rustc_transmute::Answer::No(reason) => {
+ Answer::No(reason) => {
let dst = trait_ref.substs.type_at(0);
let src = trait_ref.substs.type_at(1);
- let custom_err_msg = format!(
+ let err_msg = format!(
"`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`"
);
- let reason_msg = match reason {
+ let safe_transmute_explanation = match reason {
rustc_transmute::Reason::SrcIsUnspecified => {
format!("`{src}` does not have a well-specified layout")
}
@@ -2779,19 +2911,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
rustc_transmute::Reason::DstIsPrivate => format!(
"`{dst}` is or contains a type or field that is not visible in that scope"
),
- // FIXME(bryangarza): Include the number of bytes of src and dst
rustc_transmute::Reason::DstIsTooBig => {
format!("The size of `{src}` is smaller than the size of `{dst}`")
}
+ rustc_transmute::Reason::DstHasStricterAlignment {
+ src_min_align,
+ dst_min_align,
+ } => {
+ format!(
+ "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})"
+ )
+ }
+ rustc_transmute::Reason::DstIsMoreUnique => {
+ format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
+ }
+ // Already reported by rustc
+ rustc_transmute::Reason::TypeError => {
+ return GetSafeTransmuteErrorAndReason::Silent;
+ }
+ rustc_transmute::Reason::SrcLayoutUnknown => {
+ format!("`{src}` has an unknown layout")
+ }
+ rustc_transmute::Reason::DstLayoutUnknown => {
+ format!("`{dst}` has an unknown layout")
+ }
};
- (custom_err_msg, Some(reason_msg))
+ GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
}
// Should never get a Yes at this point! We already ran it before, and did not get a Yes.
- rustc_transmute::Answer::Yes => span_bug!(
+ Answer::Yes => span_bug!(
span,
"Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
),
- _ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
+ other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
}
}
@@ -3049,6 +3201,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
}
+ fn report_opaque_type_auto_trait_leakage(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ def_id: DefId,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let name = match self.tcx.opaque_type_origin(def_id.expect_local()) {
+ hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => {
+ format!("opaque type")
+ }
+ hir::OpaqueTyOrigin::TyAlias { .. } => {
+ format!("`{}`", self.tcx.def_path_debug_str(def_id))
+ }
+ };
+ let mut err = self.tcx.sess.struct_span_err(
+ obligation.cause.span,
+ format!("cannot check whether the hidden type of {name} satisfies auto traits"),
+ );
+ err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
+ match self.defining_use_anchor {
+ DefiningAnchor::Bubble | DefiningAnchor::Error => {}
+ DefiningAnchor::Bind(bind) => {
+ err.span_note(
+ self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)),
+ "this item depends on auto traits of the hidden type, \
+ but may also be registering the hidden type. \
+ This is not supported right now. \
+ You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(),
+ );
+ }
+ };
+ err
+ }
+
fn report_type_parameter_mismatch_error(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -3178,7 +3363,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::ConstEvaluatable(ct) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
let ty::ConstKind::Unevaluated(uv) = ct.kind() else {
bug!("const evaluatable failed for non-unevaluated const `{ct:?}`");
};
@@ -3360,3 +3545,16 @@ pub enum DefIdOrName {
DefId(DefId),
Name(&'static str),
}
+
+pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) {
+ infcx.probe(|_| {
+ let goal = Goal { predicate: o.predicate, param_env: o.param_env };
+ let tree = infcx
+ .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No))
+ .1
+ .expect("proof tree should have been generated");
+ let mut lock = std::io::stdout().lock();
+ let _ = lock.write_fmt(format_args!("{tree:?}"));
+ let _ = lock.flush();
+ });
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 10bd027b6..b16d2eb5f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -41,7 +41,6 @@ pub trait TypeErrCtxtExt<'tcx> {
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
kw::SelfUpper,
sym::ItemContext,
- sym::from_method,
sym::from_desugaring,
sym::direct,
sym::cause,
@@ -172,23 +171,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- if let ObligationCauseCode::ItemObligation(item)
- | ObligationCauseCode::BindingObligation(item, _)
- | ObligationCauseCode::ExprItemObligation(item, ..)
- | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code()
- {
- // FIXME: maybe also have some way of handling methods
- // from other traits? That would require name resolution,
- // which we might want to be some sort of hygienic.
- //
- // Currently I'm leaving it for what I need for `try`.
- if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) {
- let method = self.tcx.item_name(item);
- flags.push((sym::from_method, None));
- flags.push((sym::from_method, Some(method.to_string())));
- }
- }
-
if let Some(k) = obligation.cause.span.desugaring_kind() {
flags.push((sym::from_desugaring, None));
flags.push((sym::from_desugaring, Some(format!("{:?}", k))));
@@ -278,7 +260,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
if let ty::Array(aty, len) = self_ty.kind() {
flags.push((sym::_Self, Some("[]".to_string())));
- let len = len.kind().try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
+ let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx));
flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
@@ -672,7 +654,7 @@ impl<'tcx> OnUnimplementedFormatString {
None => {
if let Some(val) = options.get(&s) {
val
- } else if s == sym::from_desugaring || s == sym::from_method {
+ } else if s == sym::from_desugaring {
// don't break messages using these two arguments incorrectly
&empty_string
} else if s == sym::ItemContext {
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 82bad96ea..9ac1ba027 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan, Style,
+ ErrorGuaranteed, MultiSpan, Style, SuggestionStyle,
};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
@@ -38,6 +38,7 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
+use std::borrow::Cow;
use std::iter;
use std::ops::Deref;
@@ -186,7 +187,12 @@ pub trait TypeErrCtxtExt<'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
- fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>;
+ fn get_closure_name(
+ &self,
+ def_id: DefId,
+ err: &mut Diagnostic,
+ msg: Cow<'static, str>,
+ ) -> Option<Symbol>;
fn suggest_fn_call(
&self,
@@ -356,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
+
+ fn suggest_option_method_if_applicable(
+ &self,
+ failed_pred: ty::Predicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ );
+
fn note_function_argument_obligation(
&self,
body_id: LocalDefId,
@@ -771,7 +786,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Some(steps) =
autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
// Re-add the `&`
- let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+ let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl });
// Remapping bound vars here
let real_trait_pred_and_ty =
@@ -857,7 +872,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
/// Given a closure's `DefId`, return the given name of the closure.
///
/// This doesn't account for reassignments, but it's only used for suggestions.
- fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> {
+ fn get_closure_name(
+ &self,
+ def_id: DefId,
+ err: &mut Diagnostic,
+ msg: Cow<'static, str>,
+ ) -> Option<Symbol> {
let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
// Get the local name of this closure. This can be inaccurate because
// of the possibility of reassignment, but this should be good enough.
@@ -900,7 +920,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return false;
}
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
// Don't suggest calling to turn an unsized type into a sized type
@@ -934,17 +954,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = match def_id_or_name {
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
DefKind::Ctor(CtorOf::Struct, _) => {
- "use parentheses to construct this tuple struct".to_string()
+ Cow::from("use parentheses to construct this tuple struct")
}
DefKind::Ctor(CtorOf::Variant, _) => {
- "use parentheses to construct this tuple variant".to_string()
+ Cow::from("use parentheses to construct this tuple variant")
}
- kind => format!(
+ kind => Cow::from(format!(
"use parentheses to call this {}",
self.tcx.def_kind_descr(kind, def_id)
- ),
+ )),
},
- DefIdOrName::Name(name) => format!("use parentheses to call this {name}"),
+ DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),
};
let args = inputs
@@ -979,7 +999,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
..
})) => {
err.span_label(*fn_decl_span, "consider calling this closure");
- let Some(name) = self.get_closure_name(def_id, err, &msg) else {
+ let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {
return false;
};
name.to_string()
@@ -1137,7 +1157,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
@@ -1181,7 +1201,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
DefIdOrName::Name("type parameter")
};
param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be substs[1]
@@ -1278,13 +1298,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
(
trait_pred,
- self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
)
});
let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
(
trait_pred,
- self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
+ Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),
)
});
@@ -1341,7 +1361,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note(msg);
} else {
err.message =
- vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
+ vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)];
}
err.span_label(
span,
@@ -1445,7 +1465,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
) {
let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; };
let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; };
- let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty);
+ let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);
for predicate in predicates.iter() {
if !self.predicate_must_hold_modulo_regions(
@@ -1619,7 +1639,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
// FIXME: account for associated `async fn`s.
if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
obligation.predicate.kind().skip_binder()
{
err.span_label(*span, format!("this call returns `{}`", pred.self_ty()));
@@ -1686,8 +1706,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
{
let suggested_ty = match mutability {
- hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
- hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
+ hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type),
+ hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type),
};
// Remapping bound vars here
@@ -1931,7 +1951,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
),
};
- infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig))
+ Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig))
}
let argument_kind = match expected.skip_binder().self_ty().kind() {
@@ -1981,7 +2001,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
&& let Some(pred) = predicates.predicates.get(*idx)
- && let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder()
+ && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder()
&& self.tcx.is_fn_trait(trait_pred.def_id())
{
let expected_self =
@@ -1995,7 +2015,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
+ ty::ClauseKind::Trait(trait_pred)
if self.tcx.is_fn_trait(trait_pred.def_id())
&& other_idx != idx
// Make sure that the self type matches
@@ -2121,7 +2141,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// bound was introduced. At least one generator should be present for this diagnostic to be
// modified.
let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(p)) => (Some(p), Some(p.self_ty())),
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())),
_ => (None, None),
};
let mut generator = None;
@@ -2643,8 +2663,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::LetElse
| ObligationCauseCode::BinOp { .. }
| ObligationCauseCode::AscribeUserTypeProvePredicate(..)
- | ObligationCauseCode::RustCall
- | ObligationCauseCode::DropImpl => {}
+ | ObligationCauseCode::DropImpl
+ | ObligationCauseCode::ConstParam(_) => {}
+ ObligationCauseCode::RustCall => {
+ if let Some(pred) = predicate.to_opt_poly_trait_pred()
+ && Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
+ {
+ err.note("argument required to be sized due to `extern \"rust-call\"` ABI");
+ }
+ }
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
@@ -2697,6 +2724,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
err.span_note(multispan, descr);
+ if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
+ && let ty::ClauseKind::Trait(trait_pred) = clause
+ {
+ let def_id = trait_pred.def_id();
+ let visible_item = if let Some(local) = def_id.as_local() {
+ // Check for local traits being reachable.
+ let vis = &self.tcx.resolutions(()).effective_visibilities;
+ // Account for non-`pub` traits in the root of the local crate.
+ let is_locally_reachable = self.tcx.parent(def_id).is_crate_root();
+ vis.is_reachable(local) || is_locally_reachable
+ } else {
+ // Check for foreign traits being reachable.
+ self.tcx.visible_parent_map(()).get(&def_id).is_some()
+ };
+ if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item {
+ // FIXME(estebank): extend this to search for all the types that do
+ // implement this trait and list them.
+ err.note(format!(
+ "`{short_item_name}` is a \"sealed trait\", because to implement \
+ it you also need to implelement `{}`, which is not accessible; \
+ this is usually done to force you to use one of the provided \
+ types that already implement it",
+ with_no_trimmed_paths!(tcx.def_path_str(def_id)),
+ ));
+ }
+ }
} else {
err.span_note(tcx.def_span(item_def_id), descr);
}
@@ -2786,10 +2839,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.help("unsized locals are gated as an unstable feature");
}
}
- ObligationCauseCode::SizedArgumentType(sp) => {
- if let Some(span) = sp {
+ ObligationCauseCode::SizedArgumentType(ty_span) => {
+ if let Some(span) = ty_span {
if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
- && let ty::Clause::Trait(trait_pred) = clause
+ && let ty::ClauseKind::Trait(trait_pred) = clause
&& let ty::Dynamic(..) = trait_pred.self_ty().kind()
{
let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
@@ -2958,7 +3011,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for ty in bound_tys.skip_binder() {
with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap());
}
- err.note(msg.trim_end_matches(", "))
+ err.note(msg.trim_end_matches(", ").to_string())
}
ty::GeneratorWitnessMIR(def_id, substs) => {
use std::fmt::Write;
@@ -2972,7 +3025,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let ty = bty.subst(tcx, substs);
write!(msg, "`{}`, ", ty).unwrap();
}
- err.note(msg.trim_end_matches(", "))
+ err.note(msg.trim_end_matches(", ").to_string())
}
ty::Generator(def_id, _, _) => {
let sp = self.tcx.def_span(def_id);
@@ -3171,6 +3224,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)
});
}
+ ObligationCauseCode::TypeAlias(ref nested, span, def_id) => {
+ // #74711: avoid a stack overflow
+ ensure_sufficient_stack(|| {
+ self.note_obligation_cause_code(
+ body_id,
+ err,
+ predicate,
+ param_env,
+ nested,
+ obligated_types,
+ seen_requirements,
+ )
+ });
+ let mut multispan = MultiSpan::from(span);
+ multispan.push_span_label(span, "required by this bound");
+ err.span_note(
+ multispan,
+ format!(
+ "required by a bound on the type alias `{}`",
+ self.infcx.tcx.item_name(def_id)
+ ),
+ );
+ }
ObligationCauseCode::FunctionArgumentObligation {
arg_hir_id,
call_hir_id,
@@ -3271,7 +3347,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
// `<T as Future>::Output`
let projection_ty = trait_pred.map_bound(|trait_pred| {
- self.tcx.mk_projection(
+ Ty::new_projection(
+ self.tcx,
item_def_id,
// Future::Output has no substs
[trait_pred.self_ty()],
@@ -3425,7 +3502,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
let expr = expr.peel_blocks();
- let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc());
+ let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,));
let span = expr.span;
if Some(span) != err.span.primary_span() {
err.span_label(
@@ -3450,7 +3527,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
&& let Some(where_pred) = where_clauses.predicates.get(*idx)
{
- if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
+ if let Some(where_pred) = where_pred.as_trait_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
let where_pred = self.instantiate_binder_with_placeholders(where_pred);
@@ -3473,13 +3550,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
})
};
- } else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
+ } else if let Some(where_pred) = where_pred.as_projection_clause()
&& let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
&& let Some(found) = failed_pred.skip_binder().term.ty()
{
type_diffs = vec![
Sorts(ty::error::ExpectedFound {
- expected: self.tcx.mk_alias(ty::Projection, where_pred.skip_binder().projection_ty),
+ expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty),
found,
}),
];
@@ -3509,15 +3586,93 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.replace_span_with(path.ident.span, true);
}
}
- if let Some(Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::Call(hir::Expr { span, .. }, _)
- | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..),
- ..
- })) = hir.find(call_hir_id)
+
+ if let Some(Node::Expr(expr)) = hir.find(call_hir_id) {
+ if let hir::ExprKind::Call(hir::Expr { span, .. }, _)
+ | hir::ExprKind::MethodCall(
+ hir::PathSegment { ident: Ident { span, .. }, .. },
+ ..,
+ ) = expr.kind
+ {
+ if Some(*span) != err.span.primary_span() {
+ err.span_label(*span, "required by a bound introduced by this call");
+ }
+ }
+
+ if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind {
+ self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr);
+ }
+ }
+ }
+
+ fn suggest_option_method_if_applicable(
+ &self,
+ failed_pred: ty::Predicate<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ ) {
+ let tcx = self.tcx;
+ let infcx = self.infcx;
+ let Some(typeck_results) = self.typeck_results.as_ref() else { return };
+
+ // Make sure we're dealing with the `Option` type.
+ let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return };
+ if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) {
+ return;
+ }
+
+ // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`,
+ // then suggest `Option::as_deref(_mut)` if `U` can deref to `T`
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. }))
+ = failed_pred.kind().skip_binder()
+ && tcx.is_fn_trait(trait_ref.def_id)
+ && let [self_ty, found_ty] = trait_ref.substs.as_slice()
+ && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn())
+ && let fn_sig @ ty::FnSig {
+ abi: abi::Abi::Rust,
+ c_variadic: false,
+ unsafety: hir::Unsafety::Normal,
+ ..
+ } = fn_ty.fn_sig(tcx).skip_binder()
+
+ // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T`
+ && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind())
+ && !target_ty.has_escaping_bound_vars()
+
+ // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U`
+ && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind)
+ && let &[found_ty] = tys.as_slice()
+ && !found_ty.has_escaping_bound_vars()
+
+ // Extract `<U as Deref>::Target` assoc type and check that it is `T`
+ && let Some(deref_target_did) = tcx.lang_items().deref_target()
+ && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)]))
+ && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection)
+ && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation))
+ && infcx.can_eq(param_env, deref_target, target_ty)
{
- if Some(*span) != err.span.primary_span() {
- err.span_label(*span, "required by a bound introduced by this call");
+ let help = if let hir::Mutability::Mut = needs_mut
+ && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait()
+ && infcx
+ .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env)
+ .must_apply_modulo_regions()
+ {
+ Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()"))
+ } else if let hir::Mutability::Not = needs_mut {
+ Some(("call `Option::as_deref()` first", ".as_deref()"))
+ } else {
+ None
+ };
+
+ if let Some((msg, sugg)) = help {
+ err.span_suggestion_with_style(
+ expr.span.shrink_to_hi(),
+ msg,
+ sugg,
+ Applicability::MaybeIncorrect,
+ SuggestionStyle::ShowAlways
+ );
}
}
}
@@ -3539,7 +3694,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut assocs = vec![];
let mut expr = expr;
let mut prev_ty = self.resolve_vars_if_possible(
- typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
// Point at every method call in the chain with the resulting type.
@@ -3550,7 +3705,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
assocs.push(assocs_in_this_method);
prev_ty = self.resolve_vars_if_possible(
- typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
);
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
@@ -3568,7 +3723,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let hir::Node::Param(param) = parent {
// ...and it is a an fn argument.
let prev_ty = self.resolve_vars_if_possible(
- typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error_misc()),
+ typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)),
);
let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
if assocs_in_this_method.iter().any(|a| a.is_some()) {
@@ -3671,7 +3826,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
- let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
+ let ocx = ObligationCtxt::new(self.infcx);
let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
for diff in type_diffs {
let Sorts(expected_found) = diff else { continue; };
@@ -3698,12 +3853,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// in. For example, this would be what `Iterator::Item` is here.
let ty_var = self.infcx.next_ty_var(origin);
// This corresponds to `<ExprTy as Iterator>::Item = _`.
- let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
- ty::ProjectionPredicate {
+ let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::Projection(ty::ProjectionPredicate {
projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs),
term: ty_var.into(),
- },
- )));
+ }),
+ ));
let body_def_id = self.tcx.hir().enclosing_body_owner(body_id);
// Add `<ExprTy as Iterator>::Item = _` obligation.
ocx.register_obligation(Obligation::misc(
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 2f85c32b5..cf9d9315f 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::ProjectionCacheKey;
-use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation};
+use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -50,20 +50,15 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
/// method `select_all_or_error` can be used to report any remaining
/// ambiguous cases as errors.
pub struct FulfillmentContext<'tcx> {
- // A list of all obligations that have been registered with this
- // fulfillment context.
+ /// A list of all obligations that have been registered with this
+ /// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
- // Is it OK to register obligations into this infcx inside
- // an infcx snapshot?
- //
- // The "primary fulfillment" in many cases in typeck lives
- // outside of any snapshot, so any use of it inside a snapshot
- // will lead to trouble and therefore is checked against, but
- // other fulfillment contexts sometimes do live inside of
- // a snapshot (they don't *straddle* a snapshot, so there
- // is no trouble there).
- usable_in_snapshot: bool,
+ /// The snapshot in which this context was created. Using the context
+ /// outside of this snapshot leads to subtle bugs if the snapshot
+ /// gets rolled back. Because of this we explicitly check that we only
+ /// use the context in exactly this snapshot.
+ usable_in_snapshot: usize,
}
#[derive(Clone, Debug)]
@@ -80,18 +75,17 @@ pub struct PendingPredicateObligation<'tcx> {
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(PendingPredicateObligation<'_>, 72);
-impl<'a, 'tcx> FulfillmentContext<'tcx> {
+impl<'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
- pub(super) fn new() -> FulfillmentContext<'tcx> {
- FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false }
- }
-
- pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
- FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
+ pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> {
+ FulfillmentContext {
+ predicates: ObligationForest::new(),
+ usable_in_snapshot: infcx.num_open_snapshots(),
+ }
}
/// Attempts to select obligations using `selcx`.
- fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
let _enter = span.enter();
@@ -116,19 +110,19 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
+ #[inline]
fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
let obligation = infcx.resolve_vars_if_possible(obligation);
debug!(?obligation, "register_predicate_obligation");
- assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
-
self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
@@ -332,7 +326,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
// Evaluation will discard candidates using the leak check.
// This means we need to pass it the bound version of our
// predicate.
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) => {
let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref));
self.process_trait_obligation(
@@ -341,7 +335,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
&mut pending_obligation.stalled_on,
)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
let project_obligation = obligation.with(infcx.tcx, binder.rebind(data));
self.process_projection_obligation(
@@ -350,30 +344,27 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
&mut pending_obligation.stalled_on,
)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::WellFormed(_)
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..) => {
let pred =
ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder));
ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)]))
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
},
Some(pred) => match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data));
self.process_trait_obligation(
@@ -383,7 +374,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
)
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => {
if infcx.considering_regions {
infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data));
}
@@ -391,7 +382,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ProcessResult::Changed(vec![])
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
t_a,
r_b,
))) => {
@@ -401,7 +392,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ProcessResult::Changed(vec![])
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => {
let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data));
self.process_projection_obligation(
@@ -432,7 +423,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
match wf::obligations(
self.selcx.infcx,
obligation.param_env,
@@ -497,7 +488,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(uv) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.selcx.infcx,
uv,
@@ -537,7 +528,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2);
use rustc_hir::def::DefKind;
- use ty::ConstKind::Unevaluated;
+ use ty::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -633,13 +624,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
}
}
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for Chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
DefineOpaqueTypes::No,
ct.ty(),
@@ -679,7 +667,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
fn process_trait_obligation(
&mut self,
obligation: &PredicateObligation<'tcx>,
- trait_obligation: TraitObligation<'tcx>,
+ trait_obligation: PolyTraitObligation<'tcx>,
stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>,
) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> {
let infcx = self.selcx.infcx;
@@ -695,7 +683,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> {
}
}
- match self.selcx.select(&trait_obligation) {
+ match self.selcx.poly_select(&trait_obligation) {
Ok(Some(impl_source)) => {
debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth);
ProcessResult::Changed(mk_pending(impl_source.nested_obligations()))
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 2210ef975..e9cfd63e2 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
| ty::Str
| ty::Array(..)
| ty::Slice(_)
- | ty::Ref(.., hir::Mutability::Not) => return Ok(()),
+ | ty::Ref(.., hir::Mutability::Not)
+ | ty::Tuple(_) => return Ok(()),
&ty::Adt(adt, substs) => (adt, substs),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index f265230ff..1af8323b6 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -3,7 +3,6 @@
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
pub mod auto_trait;
-mod chalk_fulfill;
pub(crate) mod coherence;
pub mod const_evaluatable;
mod engine;
@@ -12,14 +11,16 @@ mod fulfill;
pub mod misc;
mod object_safety;
pub mod outlives_bounds;
-mod project;
+pub mod project;
pub mod query;
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod select;
mod specialize;
mod structural_match;
mod structural_normalize;
+#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))]
mod util;
-mod vtable;
+pub mod vtable;
pub mod wf;
use crate::infer::outlives::env::OutlivesEnvironment;
@@ -30,7 +31,7 @@ use rustc_errors::ErrorGuaranteed;
use rustc_middle::query::Providers;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_span::def_id::DefId;
use rustc_span::Span;
@@ -38,6 +39,8 @@ use rustc_span::Span;
use std::fmt::Debug;
use std::ops::ControlFlow;
+pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+
pub use self::FulfillmentErrorCode::*;
pub use self::ImplSource::*;
pub use self::ObligationCauseCode::*;
@@ -60,19 +63,15 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{
specialization_graph, translate_substs, translate_substs_with_cause, OverlapError,
};
-pub use self::structural_match::{
- search_for_adt_const_param_violation, search_for_structural_match_violation,
-};
+pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_normalize::StructurallyNormalizeExt;
pub use self::util::elaborate;
-pub use self::util::{expand_trait_aliases, TraitAliasExpander};
-pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use self::util::{
- supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item,
- SupertraitDefIds,
+ check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds,
+ transitive_bounds_that_define_assoc_item, SupertraitDefIds,
};
-
-pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext;
+pub use self::util::{expand_trait_aliases, TraitAliasExpander};
+pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
pub use rustc_infer::traits::*;
@@ -114,11 +113,11 @@ pub fn predicates_for_generics<'tcx>(
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
- generic_bounds.into_iter().enumerate().map(move |(idx, (predicate, span))| Obligation {
+ generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation {
cause: cause(idx, span),
recursion_depth: 0,
param_env,
- predicate,
+ predicate: clause.as_predicate(),
})
}
@@ -161,7 +160,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>(
// the we do no inference in the process of checking this obligation.
let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
infcx.probe(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
@@ -185,8 +184,8 @@ fn do_normalize_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
cause: ObligationCause<'tcx>,
elaborated_env: ty::ParamEnv<'tcx>,
- predicates: Vec<ty::Predicate<'tcx>>,
-) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> {
+ predicates: Vec<ty::Clause<'tcx>>,
+) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> {
let span = cause.span;
// FIXME. We should really... do something with these region
// obligations. But this call just continues the older
@@ -270,13 +269,67 @@ pub fn normalize_param_env_or_error<'tcx>(
// parameter environments once for every fn as it goes,
// and errors will get reported then; so outside of type inference we
// can be sure that no errors should occur.
- let mut predicates: Vec<_> =
- util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect();
+ let mut predicates: Vec<_> = util::elaborate(
+ tcx,
+ unnormalized_env.caller_bounds().into_iter().map(|predicate| {
+ if tcx.features().generic_const_exprs {
+ return predicate;
+ }
+
+ struct ConstNormalizer<'tcx>(TyCtxt<'tcx>);
+
+ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> {
+ fn interner(&self) -> TyCtxt<'tcx> {
+ self.0
+ }
+
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ // While it is pretty sus to be evaluating things with an empty param env, it
+ // should actually be okay since without `feature(generic_const_exprs)` the only
+ // const arguments that have a non-empty param env are array repeat counts. These
+ // do not appear in the type system though.
+ c.eval(self.0, ty::ParamEnv::empty())
+ }
+ }
+
+ // This whole normalization step is a hack to work around the fact that
+ // `normalize_param_env_or_error` is fundamentally broken from using an
+ // unnormalized param env with a trait solver that expects the param env
+ // to be normalized.
+ //
+ // When normalizing the param env we can end up evaluating obligations
+ // that have been normalized but can only be proven via a where clause
+ // which is still in its unnormalized form. example:
+ //
+ // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env
+ // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because
+ // we first normalize obligations before proving them so we end up proving
+ // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8`
+ // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that
+ // we cannot prove `T: Trait<u8>`.
+ //
+ // The same thing is true for const generics- attempting to prove
+ // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses
+ // will fail. After normalization we may be attempting to prove `T: Trait<4>` with
+ // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order
+ // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)`
+ // but as we do not have lazy norm implemented, equating the two consts fails outright.
+ //
+ // Ideally we would not normalize consts here at all but it is required for backwards
+ // compatibility. Eventually when lazy norm is implemented this can just be removed.
+ // We do not normalize types here as there is no backwards compatibility requirement
+ // for us to do so.
+ //
+ // FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality
+ predicate.fold_with(&mut ConstNormalizer(tcx))
+ }),
+ )
+ .collect();
debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates);
let elaborated_env = ty::ParamEnv::new(
- tcx.mk_predicates(&predicates),
+ tcx.mk_clauses(&predicates),
unnormalized_env.reveal(),
unnormalized_env.constness(),
);
@@ -300,11 +353,8 @@ pub fn normalize_param_env_or_error<'tcx>(
// This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
- .drain_filter(|predicate| {
- matches!(
- predicate.kind().skip_binder(),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- )
+ .extract_if(|predicate| {
+ matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..))
})
.collect();
@@ -330,7 +380,7 @@ pub fn normalize_param_env_or_error<'tcx>(
// predicates here anyway. Keeping them here anyway because it seems safer.
let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned();
let outlives_env = ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(outlives_env),
+ tcx.mk_clauses_from_iter(outlives_env),
unnormalized_env.reveal(),
unnormalized_env.constness(),
);
@@ -350,13 +400,18 @@ pub fn normalize_param_env_or_error<'tcx>(
predicates.extend(outlives_predicates);
debug!("normalize_param_env_or_error: final predicates={:?}", predicates);
ty::ParamEnv::new(
- tcx.mk_predicates(&predicates),
+ tcx.mk_clauses(&predicates),
unnormalized_env.reveal(),
unnormalized_env.constness(),
)
}
-/// Normalize a type and process all resulting obligations, returning any errors
+/// Normalize a type and process all resulting obligations, returning any errors.
+///
+/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize`
+/// which has the same behavior with the new solver. Because using a separate
+/// fulfillment context worsens caching in the old solver, `At::deeply_normalize`
+/// is still lazy with the old solver as it otherwise negatively impacts perf.
#[instrument(skip_all)]
pub fn fully_normalize<'tcx, T>(
infcx: &InferCtxt<'tcx>,
@@ -385,10 +440,7 @@ where
/// Normalizes the predicates and checks whether they hold in an empty environment. If this
/// returns true, then either normalize encountered an error or one of the predicates did not
/// hold. Used when creating vtables to check for unsatisfiable methods.
-pub fn impossible_predicates<'tcx>(
- tcx: TyCtxt<'tcx>,
- predicates: Vec<ty::Predicate<'tcx>>,
-) -> bool {
+pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool {
debug!("impossible_predicates(predicates={:?})", predicates);
let infcx = tcx.infer_ctxt().build();
@@ -485,7 +537,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI
tcx,
ObligationCause::dummy_with_span(*span),
param_env,
- ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs),
+ ty::EarlyBinder::bind(*pred).subst(tcx, impl_trait_ref.substs),
)
})
});
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index c81bf6ebc..c31944c16 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -101,7 +101,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A
debug_assert!(tcx.generics_of(trait_def_id).has_self);
debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method);
// Any method that has a `Self: Sized` bound cannot be called.
- if generics_require_sized_self(tcx, method.def_id) {
+ if tcx.generics_require_sized_self(method.def_id) {
return false;
}
@@ -115,15 +115,11 @@ fn object_safety_violations_for_trait(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
) -> Vec<ObjectSafetyViolation> {
- // Check methods for violations.
+ // Check assoc items for violations.
let mut violations: Vec<_> = tcx
.associated_items(trait_def_id)
.in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Fn)
- .filter_map(|&item| {
- object_safety_violation_for_method(tcx, trait_def_id, item)
- .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span))
- })
+ .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item))
.collect();
// Check the trait itself.
@@ -145,30 +141,6 @@ fn object_safety_violations_for_trait(
violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans));
}
- violations.extend(
- tcx.associated_items(trait_def_id)
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Const)
- .map(|item| {
- let ident = item.ident(tcx);
- ObjectSafetyViolation::AssocConst(ident.name, ident.span)
- }),
- );
-
- if !tcx.features().generic_associated_types_extended {
- violations.extend(
- tcx.associated_items(trait_def_id)
- .in_definition_order()
- .filter(|item| item.kind == ty::AssocKind::Type)
- .filter(|item| !tcx.generics_of(item.def_id).params.is_empty())
- .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none())
- .map(|item| {
- let ident = item.ident(tcx);
- ObjectSafetyViolation::GAT(ident.name, ident.span)
- }),
- );
- }
-
debug!(
"object_safety_violations_for_trait(trait_def_id={:?}) = {:?}",
trait_def_id, violations
@@ -299,22 +271,22 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
.in_definition_order()
.filter(|item| item.kind == ty::AssocKind::Type)
.flat_map(|item| tcx.explicit_item_bounds(item.def_id).subst_identity_iter_copied())
- .filter_map(|pred_span| predicate_references_self(tcx, pred_span))
+ .filter_map(|c| predicate_references_self(tcx, c))
.collect()
}
fn predicate_references_self<'tcx>(
tcx: TyCtxt<'tcx>,
- (predicate, sp): (ty::Predicate<'tcx>, Span),
+ (predicate, sp): (ty::Clause<'tcx>, Span),
) -> Option<Span> {
let self_ty = tcx.types.self_param;
let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => {
+ ty::ClauseKind::Trait(ref data) => {
// In the case of a trait predicate, we can skip the "self" type.
data.trait_ref.substs[1..].iter().any(has_self_ty).then_some(sp)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => {
+ ty::ClauseKind::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
@@ -332,24 +304,14 @@ fn predicate_references_self<'tcx>(
// possible alternatives.
data.projection_ty.substs[1..].iter().any(has_self_ty).then_some(sp)
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(_ct, ty)) => {
- has_self_ty(&ty.into()).then_some(sp)
- }
-
- ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"),
+ ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
- ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
+ ty::ClauseKind::WellFormed(..)
+ | ty::ClauseKind::TypeOutlives(..)
+ | ty::ClauseKind::RegionOutlives(..)
// FIXME(generic_const_exprs): this can mention `Self`
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
+ | ty::ClauseKind::ConstEvaluatable(..)
+ => None,
}
}
@@ -369,7 +331,7 @@ fn super_predicates_have_non_lifetime_binders(
}
fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
- generics_require_sized_self(tcx, trait_def_id)
+ tcx.generics_require_sized_self(trait_def_id)
}
fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
@@ -381,54 +343,66 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let predicates = tcx.predicates_of(def_id);
let predicates = predicates.instantiate_identity(tcx).predicates;
elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => {
+ ty::ClauseKind::Trait(ref trait_pred) => {
trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0)
}
- ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..))
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => false,
+ ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::TypeOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => false,
})
}
-/// Returns `Some(_)` if this method makes the containing trait not object safe.
-fn object_safety_violation_for_method(
+/// Returns `Some(_)` if this item makes the containing trait not object safe.
+#[instrument(level = "debug", skip(tcx), ret)]
+fn object_safety_violation_for_assoc_item(
tcx: TyCtxt<'_>,
trait_def_id: DefId,
- method: ty::AssocItem,
-) -> Option<(MethodViolationCode, Span)> {
- debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method);
- // Any method that has a `Self : Sized` requisite is otherwise
+ item: ty::AssocItem,
+) -> Option<ObjectSafetyViolation> {
+ // Any item that has a `Self : Sized` requisite is otherwise
// exempt from the regulations.
- if generics_require_sized_self(tcx, method.def_id) {
+ if tcx.generics_require_sized_self(item.def_id) {
return None;
}
- let violation = virtual_call_violation_for_method(tcx, trait_def_id, method);
- // Get an accurate span depending on the violation.
- violation.map(|v| {
- let node = tcx.hir().get_if_local(method.def_id);
- let span = match (&v, node) {
- (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
- (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
- (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
- (MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
- node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span())
+ match item.kind {
+ // Associated consts are never object safe, as they can't have `where` bounds yet at all,
+ // and associated const bounds in trait objects aren't a thing yet either.
+ ty::AssocKind::Const => {
+ Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span))
+ }
+ ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| {
+ let node = tcx.hir().get_if_local(item.def_id);
+ // Get an accurate span depending on the violation.
+ let span = match (&v, node) {
+ (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span,
+ (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span,
+ (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span,
+ (MethodViolationCode::ReferencesSelfOutput, Some(node)) => {
+ node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span())
+ }
+ _ => item.ident(tcx).span,
+ };
+
+ ObjectSafetyViolation::Method(item.name, v, span)
+ }),
+ // Associated types can only be object safe if they have `Self: Sized` bounds.
+ ty::AssocKind::Type => {
+ if !tcx.features().generic_associated_types_extended
+ && !tcx.generics_of(item.def_id).params.is_empty()
+ && item.opt_rpitit_info.is_none()
+ {
+ Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span))
+ } else {
+ // We will permit associated types if they are explicitly mentioned in the trait object.
+ // We can't check this here, as here we only check if it is guaranteed to not be possible.
+ None
}
- _ => method.ident(tcx).span,
- };
- (v, span)
- })
+ }
+ }
}
/// Returns `Some(_)` if this method cannot be called on a trait
@@ -526,7 +500,7 @@ fn virtual_call_violation_for_method<'tcx>(
// #78372
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
- format!("error: {}\n while computing layout for type {:?}", err, ty),
+ format!("error: {err}\n while computing layout for type {ty:?}"),
);
None
}
@@ -535,7 +509,7 @@ fn virtual_call_violation_for_method<'tcx>(
// e.g., `Rc<()>`
let unit_receiver_ty =
- receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
+ receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id);
match abi_of_ty(unit_receiver_ty) {
Some(Abi::Scalar(..)) => (),
@@ -583,7 +557,7 @@ fn virtual_call_violation_for_method<'tcx>(
// because a trait object can't claim to live longer than the concrete
// type. If the lifetime bound holds on dyn Trait then it's guaranteed
// to hold as well on the concrete type.
- if pred.to_opt_type_outlives().is_some() {
+ if pred.as_type_outlives_clause().is_some() {
return false;
}
@@ -600,11 +574,11 @@ fn virtual_call_violation_for_method<'tcx>(
// only if the autotrait is one of the trait object's trait bounds, like
// in `dyn Trait + AutoTrait`. This guarantees that trait objects only
// implement auto traits if the underlying type does as well.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ if let ty::ClauseKind::Trait(ty::TraitPredicate {
trait_ref: pred_trait_ref,
constness: ty::BoundConstness::NotConst,
polarity: ty::ImplPolarity::Positive,
- })) = pred.kind().skip_binder()
+ }) = pred.kind().skip_binder()
&& pred_trait_ref.self_ty() == tcx.types.self_param
&& tcx.trait_is_auto(pred_trait_ref.def_id)
{
@@ -642,7 +616,7 @@ fn receiver_for_self_ty<'tcx>(
if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) }
});
- let result = EarlyBinder(receiver_ty).subst(tcx, substs);
+ let result = EarlyBinder::bind(receiver_ty).subst(tcx, substs);
debug!(
"receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}",
receiver_ty, self_ty, method_def_id, result
@@ -690,7 +664,7 @@ fn object_ty_for_trait<'tcx>(
);
debug!(?existential_predicates);
- tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn)
+ Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
}
/// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
@@ -758,7 +732,7 @@ fn receiver_is_dispatchable<'tcx>(
// FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can
// replace this with `dyn Trait`
let unsized_self_ty: Ty<'tcx> =
- tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome"));
+ Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome"));
// `Receiver[Self => U]`
let unsized_receiver_ty =
@@ -772,7 +746,6 @@ fn receiver_is_dispatchable<'tcx>(
// Self: Unsize<U>
let unsize_predicate =
ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty])
- .without_const()
.to_predicate(tcx);
// U: Trait<Arg1, ..., ArgN>
@@ -789,7 +762,7 @@ fn receiver_is_dispatchable<'tcx>(
param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]);
ty::ParamEnv::new(
- tcx.mk_predicates_from_iter(caller_bounds),
+ tcx.mk_clauses_from_iter(caller_bounds),
param_env.reveal(),
param_env.constness(),
)
@@ -949,5 +922,10 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
}
pub fn provide(providers: &mut Providers) {
- *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers };
+ *providers = Providers {
+ object_safety_violations,
+ check_is_object_safe,
+ generics_require_sized_self,
+ ..*providers
+ };
}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index f8d056e32..ae6fc7cf8 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -9,7 +9,7 @@ use rustc_span::def_id::LocalDefId;
pub use rustc_middle::traits::query::OutlivesBound;
-type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
+pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
pub trait InferCtxtExt<'a, 'tcx> {
fn implied_outlives_bounds(
&self,
@@ -57,16 +57,12 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
let ty = OpportunisticRegionResolver::new(self).fold_ty(ty);
// We do not expect existential variables in implied bounds.
- // We may however encounter unconstrained lifetime variables in invalid
- // code. See #110161 for context.
+ // We may however encounter unconstrained lifetime variables
+ // in very rare cases.
+ //
+ // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for
+ // an example.
assert!(!ty.has_non_region_infer());
- if ty.has_infer() {
- self.tcx.sess.delay_span_bug(
- self.tcx.def_span(body_id),
- "skipped implied_outlives_bounds due to unconstrained lifetimes",
- );
- return vec![];
- }
let mut canonical_var_values = OriginalQueryValues::default();
let canonical_ty =
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 510698971..a10bca31f 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,8 +1,10 @@
//! Code for projecting associated types out of trait references.
+use super::check_substs_compatible;
use super::specialization_graph;
use super::translate_substs;
use super::util;
+use super::ImplSourceUserDefinedData;
use super::MismatchedProjectionTypes;
use super::Obligation;
use super::ObligationCause;
@@ -10,10 +12,6 @@ use super::PredicateObligation;
use super::Selection;
use super::SelectionContext;
use super::SelectionError;
-use super::{
- ImplSourceClosureData, ImplSourceFnPointerData, ImplSourceFutureData, ImplSourceGeneratorData,
- ImplSourceUserDefinedData,
-};
use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey};
use crate::errors::InherentProjectionNormalizationOverflow;
@@ -30,7 +28,9 @@ use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::at::At;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
use rustc_infer::infer::DefineOpaqueTypes;
-use rustc_infer::traits::ImplSourceBuiltinData;
+use rustc_infer::traits::FulfillmentError;
+use rustc_infer::traits::ObligationCauseCode;
+use rustc_infer::traits::TraitEngine;
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
@@ -55,14 +55,55 @@ pub trait NormalizeExt<'tcx> {
/// This normalization should be used when the type contains inference variables or the
/// projection may be fallible.
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>;
+
+ /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+ /// the current environment. In the new solver this errors in case normalization
+ /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
+ ///
+ /// In the old solver this simply uses `normalizes` and adds the nested obligations
+ /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+ /// same goals in both a temporary and the shared context which negatively impacts
+ /// performance as these don't share caching.
+ ///
+ /// FIXME(-Ztrait-solver=next): This has the same behavior as `traits::fully_normalize`
+ /// in the new solver, but because of performance reasons, we currently reuse an
+ /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+ /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+ /// rename this function to `At::fully_normalize`.
+ fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+ self,
+ value: T,
+ fulfill_cx: &mut dyn TraitEngine<'tcx>,
+ ) -> Result<T, Vec<FulfillmentError<'tcx>>>;
}
impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> {
fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
- let mut selcx = SelectionContext::new(self.infcx);
- let Normalized { value, obligations } =
- normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
- InferOk { value, obligations }
+ if self.infcx.next_trait_solver() {
+ InferOk { value, obligations: Vec::new() }
+ } else {
+ let mut selcx = SelectionContext::new(self.infcx);
+ let Normalized { value, obligations } =
+ normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+ InferOk { value, obligations }
+ }
+ }
+
+ fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+ self,
+ value: T,
+ fulfill_cx: &mut dyn TraitEngine<'tcx>,
+ ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+ if self.infcx.next_trait_solver() {
+ crate::solve::deeply_normalize(self, value)
+ } else {
+ let value = self
+ .normalize(value)
+ .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+ let errors = fulfill_cx.select_where_possible(self.infcx);
+ let value = self.infcx.resolve_vars_if_possible(value);
+ if errors.is_empty() { Ok(value) } else { Err(errors) }
+ }
}
}
@@ -406,6 +447,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
depth: usize,
obligations: &'a mut Vec<PredicateObligation<'tcx>>,
) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+ debug_assert!(!selcx.infcx.next_trait_solver());
AssocTypeNormalizer {
selcx,
param_env,
@@ -502,10 +544,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
// to make sure we don't forget to fold the substs regardless.
match kind {
- // This is really important. While we *can* handle this, this has
- // severe performance implications for large opaque types with
- // late-bound regions. See `issue-88862` benchmark.
- ty::Opaque if !data.substs.has_escaping_bound_vars() => {
+ ty::Opaque => {
// Only normalize `impl Trait` outside of type inference, usually in codegen.
match self.param_env.reveal() {
Reveal::UserFacing => ty.super_fold_with(self),
@@ -531,7 +570,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
}
}
}
- ty::Opaque => ty.super_fold_with(self),
ty::Projection if !data.has_escaping_bound_vars() => {
// This branch is *mostly* just an optimization: when we don't
@@ -621,6 +659,30 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
);
normalized_ty
}
+ ty::Weak => {
+ let infcx = self.selcx.infcx;
+ self.obligations.extend(
+ infcx
+ .tcx
+ .predicates_of(data.def_id)
+ .instantiate_own(infcx.tcx, data.substs)
+ .map(|(mut predicate, span)| {
+ if data.has_escaping_bound_vars() {
+ (predicate, ..) = BoundVarReplacer::replace_bound_vars(
+ infcx,
+ &mut self.universes,
+ predicate,
+ );
+ }
+ let mut cause = self.cause.clone();
+ cause.map_code(|code| {
+ ObligationCauseCode::TypeAlias(code, span, data.def_id)
+ });
+ Obligation::new(infcx.tcx, cause, self.param_env, predicate)
+ }),
+ );
+ infcx.tcx.type_of(data.def_id).subst(infcx.tcx, data.substs).fold_with(self)
+ }
ty::Inherent if !data.has_escaping_bound_vars() => {
// This branch is *mostly* just an optimization: when we don't
@@ -672,7 +734,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
#[instrument(skip(self), level = "debug")]
fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
let tcx = self.selcx.tcx();
- if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) {
+ if tcx.features().generic_const_exprs
+ || !needs_normalization(&constant, self.param_env.reveal())
+ {
constant
} else {
let constant = constant.super_fold_with(self);
@@ -824,7 +888,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderRegion { universe, bound: br };
self.mapped_regions.insert(p, br);
- self.infcx.tcx.mk_re_placeholder(p)
+ ty::Region::new_placeholder(self.infcx.tcx, p)
}
_ => r,
}
@@ -842,7 +906,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderType { universe, bound: bound_ty };
self.mapped_types.insert(p, bound_ty);
- self.infcx.tcx.mk_placeholder(p)
+ Ty::new_placeholder(self.infcx.tcx, p)
}
_ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
_ => t,
@@ -861,7 +925,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderConst { universe, bound: bound_const };
self.mapped_consts.insert(p, bound_const);
- self.infcx.tcx.mk_const(p, ct.ty())
+ ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
}
_ => ct.super_fold_with(self),
}
@@ -945,7 +1009,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_re_late_bound(db, *replace_var)
+ ty::Region::new_late_bound(self.interner(), db, *replace_var)
}
None => r1,
}
@@ -972,7 +1036,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_bound(db, *replace_var)
+ Ty::new_bound(self.infcx.tcx, db, *replace_var)
}
None => ty,
}
@@ -996,7 +1060,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.interner().mk_const(ty::ConstKind::Bound(db, *replace_var), ct.ty())
+ ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
}
None => ct,
}
@@ -1059,6 +1123,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Result<Option<Term<'tcx>>, InProgress> {
let infcx = selcx.infcx;
+ debug_assert!(!selcx.infcx.next_trait_solver());
// Don't use the projection cache in intercrate mode -
// the `infcx` may be re-used between intercrate in non-intercrate
// mode, which could lead to using incorrect cache results.
@@ -1168,11 +1233,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
};
let mut deduped: SsoHashSet<_> = Default::default();
- result.obligations.drain_filter(|projected_obligation| {
+ result.obligations.retain(|projected_obligation| {
if !deduped.insert(projected_obligation.clone()) {
- return true;
+ return false;
}
- false
+ true
});
if use_cache {
@@ -1375,7 +1440,7 @@ struct Progress<'tcx> {
impl<'tcx> Progress<'tcx> {
fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
- Progress { term: tcx.ty_error(guar).into(), obligations: vec![] }
+ Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] }
}
fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self {
@@ -1434,19 +1499,22 @@ fn project<'cx, 'tcx>(
ProjectionCandidateSet::None => {
let tcx = selcx.tcx();
let term = match tcx.def_kind(obligation.predicate.def_id) {
- DefKind::AssocTy | DefKind::ImplTraitPlaceholder => tcx
- .mk_projection(obligation.predicate.def_id, obligation.predicate.substs)
- .into(),
- DefKind::AssocConst => tcx
- .mk_const(
- ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(
- obligation.predicate.def_id,
- obligation.predicate.substs,
- )),
- tcx.type_of(obligation.predicate.def_id)
- .subst(tcx, obligation.predicate.substs),
- )
- .into(),
+ DefKind::AssocTy | DefKind::ImplTraitPlaceholder => Ty::new_projection(
+ tcx,
+ obligation.predicate.def_id,
+ obligation.predicate.substs,
+ )
+ .into(),
+ DefKind::AssocConst => ty::Const::new_unevaluated(
+ tcx,
+ ty::UnevaluatedConst::new(
+ obligation.predicate.def_id,
+ obligation.predicate.substs,
+ ),
+ tcx.type_of(obligation.predicate.def_id)
+ .subst(tcx, obligation.predicate.substs),
+ )
+ .into(),
kind => {
bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
}
@@ -1477,7 +1545,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>(
let trait_def_id = tcx.parent(trait_fn_def_id);
let trait_substs =
obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id));
- // FIXME(named-returns): Binders
let trait_predicate = ty::TraitRef::new(tcx, trait_def_id, trait_substs);
let _ = selcx.infcx.commit_if_ok(|_| {
@@ -1543,7 +1610,7 @@ 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.predicate.self_ty().kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, ref data) => {
tcx.item_bounds(data.def_id).subst(tcx, data.substs)
}
@@ -1584,6 +1651,10 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>(
let tcx = selcx.tcx();
+ if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object {
+ return;
+ }
+
let self_ty = obligation.predicate.self_ty();
let object_ty = selcx.infcx.shallow_resolve(self_ty);
let data = match object_ty.kind() {
@@ -1620,15 +1691,13 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>(
obligation: &ProjectionTyObligation<'tcx>,
candidate_set: &mut ProjectionCandidateSet<'tcx>,
ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>,
- env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+ env_predicates: impl Iterator<Item = ty::Clause<'tcx>>,
potentially_unnormalized_candidates: bool,
) {
let infcx = selcx.infcx;
for predicate in env_predicates {
let bound_predicate = predicate.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) =
- predicate.kind().skip_binder()
- {
+ if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() {
let data = bound_predicate.rebind(data);
if data.projection_def_id() != obligation.predicate.def_id {
continue;
@@ -1677,8 +1746,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// If we are resolving `<T as TraitRef<...>>::Item == Type`,
// start out by selecting the predicate `T as TraitRef<...>`:
- let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx()));
- let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref);
+ let trait_ref = obligation.predicate.trait_ref(selcx.tcx());
+ let trait_obligation = obligation.with(selcx.tcx(), trait_ref);
let _ = selcx.infcx.commit_if_ok(|_| {
let impl_source = match selcx.select(&trait_obligation) {
Ok(Some(impl_source)) => impl_source,
@@ -1694,11 +1763,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
};
let eligible = match &impl_source {
- super::ImplSource::Closure(_)
- | super::ImplSource::Generator(_)
- | super::ImplSource::Future(_)
- | super::ImplSource::FnPointer(_)
- | super::ImplSource::TraitAlias(_) => true,
super::ImplSource::UserDefined(impl_data) => {
// We have to be careful when projecting out of an
// impl because of specialization. If we are not in
@@ -1737,7 +1801,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
if obligation.param_env.reveal() == Reveal::All {
// NOTE(eddyb) inference variables can resolve to parameters, so
// assume `poly_trait_ref` isn't monomorphic, if it contains any.
- let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(poly_trait_ref);
+ let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(trait_ref);
!poly_trait_ref.still_further_specializable()
} else {
debug!(
@@ -1756,7 +1820,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let lang_items = selcx.tcx().lang_items();
- if lang_items.discriminant_kind_trait() == Some(poly_trait_ref.def_id()) {
+ if [lang_items.gen_trait(), lang_items.future_trait()].contains(&Some(trait_ref.def_id))
+ || selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some()
+ {
+ true
+ } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
match self_ty.kind() {
ty::Bool
| ty::Char
@@ -1791,7 +1859,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
| ty::Infer(..)
| ty::Error(_) => false,
}
- } else if lang_items.pointee_trait() == Some(poly_trait_ref.def_id()) {
+ } else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
let tail = selcx.tcx().struct_tail_with_normalize(
self_ty,
|ty| {
@@ -1866,7 +1934,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
}
}
} else {
- bug!("unexpected builtin trait with associated type: {poly_trait_ref:?}")
+ bug!("unexpected builtin trait with associated type: {trait_ref:?}")
}
}
super::ImplSource::Param(..) => {
@@ -1903,9 +1971,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
// why we special case object types.
false
}
- super::ImplSource::AutoImpl(..)
- | super::ImplSource::TraitUpcasting(_)
- | super::ImplSource::ConstDestruct(_) => {
+ | super::ImplSource::TraitUpcasting(_) => {
// These traits have no associated types.
selcx.tcx().sess.delay_span_bug(
obligation.cause.span,
@@ -1969,17 +2035,26 @@ fn confirm_select_candidate<'cx, 'tcx>(
) -> Progress<'tcx> {
match impl_source {
super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
- super::ImplSource::Generator(data) => confirm_generator_candidate(selcx, obligation, data),
- super::ImplSource::Future(data) => confirm_future_candidate(selcx, obligation, data),
- super::ImplSource::Closure(data) => confirm_closure_candidate(selcx, obligation, data),
- super::ImplSource::FnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data),
- super::ImplSource::Builtin(data) => confirm_builtin_candidate(selcx, obligation, data),
+ super::ImplSource::Builtin(data) => {
+ let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
+ let lang_items = selcx.tcx().lang_items();
+ if lang_items.gen_trait() == Some(trait_def_id) {
+ confirm_generator_candidate(selcx, obligation, data)
+ } else if lang_items.future_trait() == Some(trait_def_id) {
+ confirm_future_candidate(selcx, obligation, data)
+ } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
+ if obligation.predicate.self_ty().is_closure() {
+ confirm_closure_candidate(selcx, obligation, data)
+ } else {
+ confirm_fn_pointer_candidate(selcx, obligation, data)
+ }
+ } else {
+ confirm_builtin_candidate(selcx, obligation, data)
+ }
+ }
super::ImplSource::Object(_)
- | super::ImplSource::AutoImpl(..)
| super::ImplSource::Param(..)
- | super::ImplSource::TraitUpcasting(_)
- | super::ImplSource::TraitAlias(..)
- | super::ImplSource::ConstDestruct(_) => {
+ | super::ImplSource::TraitUpcasting(_) => {
// we don't create Select candidates with this kind of resolution
span_bug!(
obligation.cause.span,
@@ -1993,9 +2068,14 @@ fn confirm_select_candidate<'cx, 'tcx>(
fn confirm_generator_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let gen_sig = impl_source.substs.as_generator().poly_sig();
+ let ty::Generator(_, substs, _) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let gen_sig = substs.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2033,16 +2113,21 @@ fn confirm_generator_candidate<'cx, 'tcx>(
});
confirm_param_env_candidate(selcx, obligation, predicate, false)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_future_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let gen_sig = impl_source.substs.as_generator().poly_sig();
+ let ty::Generator(_, substs, _) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let gen_sig = substs.as_generator().poly_sig();
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2072,14 +2157,14 @@ fn confirm_future_candidate<'cx, 'tcx>(
});
confirm_param_env_candidate(selcx, obligation, predicate, false)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_builtin_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- data: ImplSourceBuiltinData<PredicateObligation<'tcx>>,
+ data: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
let tcx = selcx.tcx();
let self_ty = obligation.predicate.self_ty();
@@ -2127,15 +2212,15 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
.with_addl_obligations(obligations)
- .with_addl_obligations(data.nested)
+ .with_addl_obligations(data)
}
fn confirm_fn_pointer_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let fn_type = selcx.infcx.shallow_resolve(fn_pointer_impl_source.fn_ty);
+ let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
let sig = fn_type.fn_sig(selcx.tcx());
let Normalized { value: sig, obligations } = normalize_with_depth(
selcx,
@@ -2146,16 +2231,21 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
);
confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes)
- .with_addl_obligations(fn_pointer_impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
fn confirm_closure_candidate<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
- impl_source: ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>,
+ nested: Vec<PredicateObligation<'tcx>>,
) -> Progress<'tcx> {
- let closure_sig = impl_source.substs.as_closure().sig();
+ let ty::Closure(_, substs) =
+ selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
+ else {
+ unreachable!()
+ };
+ let closure_sig = substs.as_closure().sig();
let Normalized { value: closure_sig, obligations } = normalize_with_depth(
selcx,
obligation.param_env,
@@ -2167,7 +2257,7 @@ fn confirm_closure_candidate<'cx, 'tcx>(
debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate");
confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No)
- .with_addl_obligations(impl_source.nested)
+ .with_addl_obligations(nested)
.with_addl_obligations(obligations)
}
@@ -2263,7 +2353,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
obligation, poly_cache_entry, e,
);
debug!("confirm_param_env_candidate: {}", msg);
- let err = infcx.tcx.ty_error_with_message(obligation.cause.span, msg);
+ let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg);
Progress { term: err.into(), obligations: vec![] }
}
}
@@ -2295,7 +2385,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
"confirm_impl_candidate: no associated type {:?} for {:?}",
assoc_ty.item.name, obligation.predicate
);
- return Progress { term: tcx.ty_error_misc().into(), obligations: nested };
+ return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
}
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
@@ -2311,13 +2401,14 @@ fn confirm_impl_candidate<'cx, 'tcx>(
let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
let did = assoc_ty.item.def_id;
let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, did);
- let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
- ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
+ let uv = ty::UnevaluatedConst::new(did, identity_substs);
+ ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into())
} else {
ty.map_bound(|ty| ty.into())
};
if !check_substs_compatible(tcx, assoc_ty.item, substs) {
- let err = tcx.ty_error_with_message(
+ let err = Ty::new_error_with_message(
+ tcx,
obligation.cause.span,
"impl item and trait item have different parameters",
);
@@ -2328,47 +2419,6 @@ fn confirm_impl_candidate<'cx, 'tcx>(
}
}
-// Verify that the trait item and its implementation have compatible substs lists
-fn check_substs_compatible<'tcx>(
- tcx: TyCtxt<'tcx>,
- assoc_item: ty::AssocItem,
- substs: ty::SubstsRef<'tcx>,
-) -> bool {
- fn check_substs_compatible_inner<'tcx>(
- tcx: TyCtxt<'tcx>,
- generics: &'tcx ty::Generics,
- args: &'tcx [ty::GenericArg<'tcx>],
- ) -> bool {
- if generics.count() != args.len() {
- return false;
- }
-
- let (parent_args, own_args) = args.split_at(generics.parent_count);
-
- if let Some(parent) = generics.parent
- && let parent_generics = tcx.generics_of(parent)
- && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
- return false;
- }
-
- for (param, arg) in std::iter::zip(&generics.params, own_args) {
- match (&param.kind, arg.unpack()) {
- (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
- | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
- | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
- _ => return false,
- }
- }
-
- true
- }
-
- let generics = tcx.generics_of(assoc_item.def_id);
- // Chop off any additional substs (RPITIT) substs
- let substs = &substs[0..generics.count().min(substs.len())];
- check_substs_compatible_inner(tcx, generics, substs)
-}
-
fn confirm_impl_trait_in_trait_candidate<'tcx>(
selcx: &mut SelectionContext<'_, 'tcx>,
obligation: &ProjectionTyObligation<'tcx>,
@@ -2385,13 +2435,14 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
// We don't support specialization for RPITITs anyways... yet.
// Also don't try to project to an RPITIT that has no value
if !leaf_def.is_final() || !leaf_def.item.defaultness(tcx).has_value() {
- return Progress { term: tcx.ty_error_misc().into(), obligations };
+ return Progress { term: Ty::new_misc_error(tcx).into(), obligations };
}
// Use the default `impl Trait` for the trait, e.g., for a default trait body
if leaf_def.item.container == ty::AssocItemContainer::TraitContainer {
return Progress {
- term: tcx.mk_opaque(obligation.predicate.def_id, obligation.predicate.substs).into(),
+ term: Ty::new_opaque(tcx, obligation.predicate.def_id, obligation.predicate.substs)
+ .into(),
obligations,
};
}
@@ -2409,7 +2460,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
);
if !check_substs_compatible(tcx, leaf_def.item, impl_fn_substs) {
- let err = tcx.ty_error_with_message(
+ let err = Ty::new_error_with_message(
+ tcx,
obligation.cause.span,
"impl method and trait method have different parameters",
);
@@ -2455,7 +2507,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>(
cause.clone(),
obligation.recursion_depth + 1,
tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else(
- |guar| tcx.ty_error(guar),
+ |guar| Ty::new_error(tcx, guar),
|tys| tys[&obligation.predicate.def_id].subst(tcx, impl_fn_substs),
),
&mut obligations,
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 4e4172e7f..709c3f432 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -307,13 +307,13 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
// there, but that needs some way to handle cycles.
constraints
.dtorck_types
- .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
constraints
.outlives
- .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
constraints
.overflows
- .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs)));
+ .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs)));
}
// Objects must be alive in order for their destructor
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index edbe2de81..a50644bb7 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -67,7 +67,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
let mut _orig_values = OriginalQueryValues::default();
let param_env = match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
// we ignore the value set to it.
let mut _constness = pred.constness;
obligation
@@ -78,9 +78,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
_ => obligation.param_env.without_const(),
};
- if self.tcx.trait_solver_next() {
+ if self.next_trait_solver() {
self.probe(|snapshot| {
- let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
+ let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self);
fulfill_cx.register_predicate_obligation(self, obligation.clone());
// True errors
// FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?
@@ -90,13 +90,14 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
Ok(EvaluationResult::EvaluatedToAmbig)
} else if self.opaque_types_added_in_snapshot(snapshot) {
Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
- } else if self.region_constraints_added_in_snapshot(snapshot).is_some() {
+ } else if self.region_constraints_added_in_snapshot(snapshot) {
Ok(EvaluationResult::EvaluatedToOkModuloRegions)
} else {
Ok(EvaluationResult::EvaluatedToOk)
}
})
} else {
+ assert!(!self.intercrate);
let c_pred = self.canonicalize_query_keep_static(
param_env.and(obligation.predicate),
&mut _orig_values,
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 8bf934cb7..7fe79fd86 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> {
///
/// After codegen, when lifetimes do not matter, it is preferable to instead
/// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure.
- fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+ fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>;
}
@@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
/// normalizing, but for now should be used only when we actually
/// know that normalization will succeed, since error reporting
/// and other details are still "under development".
- fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
+ fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> {
self.param_env,
self.cause,
);
+
+ if self.infcx.next_trait_solver() {
+ match crate::solve::deeply_normalize(self, value) {
+ Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
+ Err(_errors) => {
+ return Err(NoSolution);
+ }
+ }
+ }
+
if !needs_normalization(&value, self.param_env.reveal()) {
return Ok(Normalized { value, obligations: vec![] });
}
@@ -211,10 +221,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
// Wrap this in a closure so we don't accidentally return from the outer function
let res = match kind {
- // This is really important. While we *can* handle this, this has
- // severe performance implications for large opaque types with
- // late-bound regions. See `issue-88862` benchmark.
- ty::Opaque if !data.substs.has_escaping_bound_vars() => {
+ ty::Opaque => {
// Only normalize `impl Trait` outside of type inference, usually in codegen.
match self.param_env.reveal() {
Reveal::UserFacing => ty.try_super_fold_with(self)?,
@@ -255,9 +262,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
}
}
- ty::Opaque => ty.try_super_fold_with(self)?,
-
- ty::Projection | ty::Inherent => {
+ ty::Projection | ty::Inherent | ty::Weak => {
// See note in `rustc_trait_selection::traits::project`
let infcx = self.infcx;
@@ -282,6 +287,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
let result = match kind {
ty::Projection => tcx.normalize_projection_ty(c_data),
+ ty::Weak => tcx.normalize_weak_ty(c_data),
ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
_ => unreachable!(),
}?;
@@ -321,8 +327,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
};
// `tcx.normalize_projection_ty` may normalize to a type that still has
// unevaluated consts, so keep normalizing here if that's the case.
- if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
- res.try_super_fold_with(self)?
+ // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
+ // and we need to continue folding it to reveal the TAIT behind it.
+ if res != ty
+ && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
+ {
+ res.try_fold_with(self)?
} else {
res
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index 01d7a1e79..44671a076 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -67,7 +67,8 @@ fn relate_mir_and_user_ty<'tcx>(
ocx.eq(&cause, param_env, mir_ty, user_ty)?;
// FIXME(#104764): We should check well-formedness before normalization.
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into())));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
Ok(())
}
@@ -119,7 +120,9 @@ fn relate_mir_and_user_substs<'tcx>(
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ impl_self_ty.into(),
+ )));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
}
@@ -134,7 +137,8 @@ fn relate_mir_and_user_substs<'tcx>(
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
- let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into()));
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into())));
ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
Ok(())
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index 6d8d2103f..5420caee3 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -5,6 +5,7 @@ use crate::traits::ObligationCtxt;
use rustc_errors::ErrorGuaranteed;
use rustc_infer::infer::region_constraints::RegionConstraintData;
use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::{TyCtxt, TypeFoldable};
use rustc_span::source_map::DUMMY_SP;
use rustc_span::Span;
@@ -24,9 +25,10 @@ impl<F> CustomTypeOp<F> {
}
}
-impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp<F>
+impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp<F>
where
F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
+ R: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>,
{
type Output = R;
/// We can't do any custom error reporting for `CustomTypeOp`, so
@@ -57,12 +59,16 @@ impl<F> fmt::Debug for CustomTypeOp<F> {
/// Executes `op` and then scrapes out all the "old style" region
/// constraints that result, creating query-region-constraints.
-pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
+pub fn scrape_region_constraints<'tcx, Op, R>(
infcx: &InferCtxt<'tcx>,
op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
name: &'static str,
span: Span,
-) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> {
+) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
+where
+ R: TypeFoldable<TyCtxt<'tcx>>,
+ Op: super::TypeOp<'tcx, Output = R>,
+{
// During NLL, we expect that nobody will register region
// obligations **except** as part of a custom type op (and, at the
// end of each custom type op, we scrape out the region
@@ -76,7 +82,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
);
let value = infcx.commit_if_ok(|_| {
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
let value = op(&ocx).map_err(|_| {
infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}"))
})?;
@@ -91,6 +97,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
}
})?;
+ // Next trait solver performs operations locally, and normalize goals should resolve vars.
+ let value = infcx.resolve_vars_if_possible(value);
+
let region_obligations = infcx.take_registered_region_obligations();
let region_constraint_data = infcx.take_and_reset_region_constraints();
let region_constraints = query_response::make_query_region_constraints(
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 9989fc9c4..979498fb6 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -1,3 +1,4 @@
+use crate::solve;
use crate::traits::query::NoSolution;
use crate::traits::wf;
use crate::traits::ObligationCtxt;
@@ -6,6 +7,7 @@ use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
use rustc_infer::traits::query::OutlivesBound;
use rustc_middle::infer::canonical::CanonicalQueryResponse;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::DUMMY_SP;
@@ -108,7 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// learn anything new from those.
if obligation.predicate.has_non_region_infer() {
match obligation.predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::AliasRelate(..) => {
ocx.register_obligation(obligation.clone());
}
@@ -121,33 +123,33 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
Some(pred) => pred,
};
match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
// FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound
// if we ever support that
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
+ => {}
// We need to search through *all* WellFormed predicates
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf_args.push(arg);
}
// We need to register region relationships
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
r_a,
r_b,
))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
ty_a,
r_b,
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
@@ -164,19 +166,29 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
// We lazily compute the outlives components as
// `select_all_or_error` constrains inference variables.
- let implied_bounds = outlives_bounds
- .into_iter()
- .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
- ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
+ let mut implied_bounds = Vec::new();
+ for ty::OutlivesPredicate(a, r_b) in outlives_bounds {
+ match a.unpack() {
+ ty::GenericArgKind::Lifetime(r_a) => {
+ implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a))
+ }
ty::GenericArgKind::Type(ty_a) => {
- let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+ let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
+ // Need to manually normalize in the new solver as `wf::obligations` does not.
+ if ocx.infcx.next_trait_solver() {
+ ty_a = solve::deeply_normalize(
+ ocx.infcx.at(&ObligationCause::dummy(), param_env),
+ ty_a,
+ )
+ .map_err(|_errs| NoSolution)?;
+ }
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);
- implied_bounds_from_components(r_b, components)
+ implied_bounds.extend(implied_bounds_from_components(r_b, components))
}
ty::GenericArgKind::Const(_) => unreachable!(),
- })
- .collect();
+ }
+ }
Ok(implied_bounds)
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 642fdec2d..9d7933e23 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -146,7 +146,7 @@ where
infcx: &InferCtxt<'tcx>,
span: Span,
) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
- if infcx.tcx.trait_solver_next() {
+ if infcx.next_trait_solver() {
return Ok(scrape_region_constraints(
infcx,
|ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self),
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
index 57ca14aa4..9559f5002 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
@@ -50,12 +50,12 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> {
}
}
-impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> {
+impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> {
fn type_op_method(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
- tcx.type_op_normalize_predicate(canonicalized)
+ tcx.type_op_normalize_clause(canonicalized)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
index 47850bc33..789ef6472 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -18,7 +18,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
// `&T`, accounts for about 60% percentage of the predicates
// we have to prove. No need to canonicalize and all that for
// such cases.
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) =
+ if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
key.value.predicate.kind().skip_binder()
{
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
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 8bc82b9f5..d5f6aaa7f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -10,7 +10,7 @@ use hir::def_id::DefId;
use hir::LangItem;
use rustc_hir as hir;
use rustc_infer::traits::ObligationCause;
-use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
+use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -137,13 +137,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_candidates_from_projected_tys(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Before we go into the whole placeholder thing, just
// quickly check if the self-type is a projection at all.
match obligation.predicate.skip_binder().trait_ref.self_ty().kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, _) => {}
ty::Infer(ty::TyVar(_)) => {
span_bug!(
@@ -181,7 +181,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.caller_bounds()
.iter()
.filter(|p| !p.references_error())
- .filter_map(|p| p.to_opt_poly_trait_pred());
+ .filter_map(|p| p.as_trait_clause());
// Micro-optimization: filter out predicates relating to different traits.
let matching_bounds =
@@ -206,7 +206,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_generator_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder because the substs on generator types never
@@ -231,7 +231,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_future_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = obligation.self_ty().skip_binder();
@@ -254,7 +254,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// unified during the confirmation step.
fn assemble_closure_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else {
@@ -292,7 +292,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Implements one of the `Fn()` family for a fn pointer.
fn assemble_fn_pointer_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// We provide impl of all fn traits for fn pointers.
@@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, candidates))]
fn assemble_candidates_from_impls(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Essentially any user-written impl will match with an error type,
@@ -360,7 +360,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
- if !drcx.substs_refs_may_unify(obligation_substs, impl_trait_ref.0.substs) {
+ if !drcx
+ .substs_refs_may_unify(obligation_substs, impl_trait_ref.skip_binder().substs)
+ {
return;
}
if self.reject_fn_ptr_impls(
@@ -386,9 +388,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// `FnPtr`, when we wanted to report that it doesn't implement `Trait`.
#[instrument(level = "trace", skip(self), ret)]
fn reject_fn_ptr_impls(
- &self,
+ &mut self,
impl_def_id: DefId,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_self_ty: Ty<'tcx>,
) -> bool {
// Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path.
@@ -400,7 +402,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
};
for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates {
- let ty::PredicateKind::Clause(ty::Clause::Trait(pred))
+ let ty::ClauseKind::Trait(pred)
= predicate.kind().skip_binder() else { continue };
if fn_ptr_trait != pred.trait_ref.def_id {
continue;
@@ -415,17 +417,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Fast path to avoid evaluating an obligation that trivially holds.
// There may be more bounds, but these are checked by the regular path.
ty::FnPtr(..) => return false,
+
// These may potentially implement `FnPtr`
ty::Placeholder(..)
| ty::Dynamic(_, _, _)
| ty::Alias(_, _)
| ty::Infer(_)
- | ty::Param(..) => {}
+ | ty::Param(..)
+ | ty::Bound(_, _) => {}
- ty::Bound(_, _) => span_bug!(
- obligation.cause.span(),
- "cannot have escaping bound var in self type of {obligation:#?}"
- ),
// These can't possibly implement `FnPtr` as they are concrete types
// and not `FnPtr`
ty::Bool
@@ -461,10 +461,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| {
pred.trait_ref =
ty::TraitRef::new(self.tcx(), fn_ptr_trait, [pred.trait_ref.self_ty()]);
- ty::PredicateKind::Clause(ty::Clause::Trait(pred))
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
})),
);
- if let Ok(r) = self.infcx.evaluate_obligation(&obligation) {
+ if let Ok(r) = self.evaluate_root_obligation(&obligation) {
if !r.may_apply() {
return true;
}
@@ -475,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidates_from_auto_impls(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder here because the tests we do below do not involve bound regions.
@@ -544,7 +544,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Searches for impls that might apply to `obligation`.
fn assemble_candidates_from_object_ty(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
debug!(
@@ -552,6 +552,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
"assemble_candidates_from_object_ty",
);
+ if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object {
+ return;
+ }
+
self.infcx.probe(|_snapshot| {
if obligation.has_non_region_late_bound() {
return;
@@ -664,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// Searches for unsizing that might apply to `obligation`.
fn assemble_candidates_for_unsizing(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// We currently never consider higher-ranked obligations e.g.
@@ -778,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_transmutability(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
if obligation.predicate.has_non_region_param() {
@@ -796,7 +800,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self, obligation, candidates))]
fn assemble_candidates_for_trait_alias(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// Okay to skip binder here because the tests we do below do not involve bound regions.
@@ -833,7 +837,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_const_destruct_candidates(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// If the predicate is `~const Destruct` in a non-const environment, we don't actually need
@@ -920,7 +924,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidate_for_tuple(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
@@ -962,7 +966,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidate_for_pointer_like(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
// The regions of a type don't affect the size of the type
@@ -987,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn assemble_candidates_for_fn_ptr_trait(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidates: &mut SelectionCandidateSet<'tcx>,
) {
let self_ty = self.infcx.shallow_resolve(obligation.self_ty());
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0d9f55d4c..7adc29bbb 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -6,6 +6,7 @@
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
+use rustc_ast::Mutability;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
@@ -13,9 +14,8 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
use rustc_middle::ty::{
self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
- TraitRef, Ty, TyCtxt, TypeVisitableExt,
+ TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
};
-use rustc_session::config::TraitSolver;
use rustc_span::def_id::DefId;
use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
@@ -26,12 +26,9 @@ use crate::traits::vtable::{
};
use crate::traits::{
BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource,
- ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData,
- ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData,
- ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData,
- ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, Obligation,
- ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, SelectionError,
- TraitNotObjectSafe, TraitObligation, Unimplemented,
+ ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized,
+ Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation,
+ PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented,
};
use super::BuiltinImplConditions;
@@ -45,7 +42,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self))]
pub(super) fn confirm_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
candidate: SelectionCandidate<'tcx>,
) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
let mut impl_src = match candidate {
@@ -70,8 +67,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
AutoImplCandidate => {
- let data = self.confirm_auto_impl_candidate(obligation);
- ImplSource::AutoImpl(data)
+ let data = self.confirm_auto_impl_candidate(obligation)?;
+ ImplSource::Builtin(data)
}
ProjectionCandidate(idx, constness) => {
@@ -86,34 +83,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
- ImplSource::Closure(vtable_closure)
+ ImplSource::Builtin(vtable_closure)
}
GeneratorCandidate => {
let vtable_generator = self.confirm_generator_candidate(obligation)?;
- ImplSource::Generator(vtable_generator)
+ ImplSource::Builtin(vtable_generator)
}
FutureCandidate => {
let vtable_future = self.confirm_future_candidate(obligation)?;
- ImplSource::Future(vtable_future)
+ ImplSource::Builtin(vtable_future)
}
FnPointerCandidate { is_const } => {
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
- ImplSource::FnPointer(data)
+ ImplSource::Builtin(data)
}
TraitAliasCandidate => {
let data = self.confirm_trait_alias_candidate(obligation);
- ImplSource::TraitAlias(data)
+ ImplSource::Builtin(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
// no additional obligations to prove and no types in particular to unify, etc.
- ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst)
+ ImplSource::Builtin(Vec::new())
}
BuiltinUnsizeCandidate => {
@@ -128,7 +125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ConstDestructCandidate(def_id) => {
let data = self.confirm_const_destruct_candidate(obligation, def_id)?;
- ImplSource::ConstDestruct(data)
+ ImplSource::Builtin(data)
}
};
@@ -151,7 +148,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_projection_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
idx: usize,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
@@ -162,7 +159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let placeholder_self_ty = placeholder_trait_predicate.self_ty();
let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate);
let (def_id, substs) = match *placeholder_self_ty.kind() {
- // Excluding IATs here as they don't have meaningful item bounds.
+ // Excluding IATs and type aliases here as they don't have meaningful item bounds.
ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
(def_id, substs)
}
@@ -171,7 +168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
let candidate = candidate_predicate
- .to_opt_poly_trait_pred()
+ .as_trait_clause()
.expect("projection candidate is not a trait predicate")
.map_bound(|t| t.trait_ref);
let mut obligations = Vec::new();
@@ -218,7 +215,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_param_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
param: ty::PolyTraitRef<'tcx>,
) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, ?param, "confirm_param_candidate");
@@ -241,9 +238,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_builtin_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
has_nested: bool,
- ) -> ImplSourceBuiltinData<PredicateObligation<'tcx>> {
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
let lang_items = self.tcx().lang_items();
@@ -276,14 +273,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligations);
- ImplSourceBuiltinData { nested: obligations }
+ obligations
}
+ #[instrument(level = "debug", skip(self))]
fn confirm_transmutability_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
- debug!(?obligation, "confirm_transmutability_candidate");
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ use rustc_transmute::{Answer, Condition};
+ #[instrument(level = "debug", skip(tcx, obligation, predicate))]
+ fn flatten_answer_tree<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
+ predicate: TraitPredicate<'tcx>,
+ cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
+ ) -> Vec<PredicateObligation<'tcx>> {
+ match cond {
+ // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
+ // Not possible until the trait solver supports disjunctions of obligations
+ Condition::IfAll(conds) | Condition::IfAny(conds) => conds
+ .into_iter()
+ .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
+ .collect(),
+ Condition::IfTransmutable { src, dst } => {
+ let trait_def_id = obligation.predicate.def_id();
+ let scope = predicate.trait_ref.substs.type_at(2);
+ let assume_const = predicate.trait_ref.substs.const_at(3);
+ let make_obl = |from_ty, to_ty| {
+ let trait_ref1 = ty::TraitRef::new(
+ tcx,
+ trait_def_id,
+ [
+ ty::GenericArg::from(to_ty),
+ ty::GenericArg::from(from_ty),
+ ty::GenericArg::from(scope),
+ ty::GenericArg::from(assume_const),
+ ],
+ );
+ Obligation::with_depth(
+ tcx,
+ obligation.cause.clone(),
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ trait_ref1,
+ )
+ };
+
+ // If Dst is mutable, check bidirectionally.
+ // For example, transmuting bool -> u8 is OK as long as you can't update that u8
+ // to be > 1, because you could later transmute the u8 back to a bool and get UB.
+ match dst.mutability {
+ Mutability::Not => vec![make_obl(src.ty, dst.ty)],
+ Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
+ }
+ }
+ }
+ }
// We erase regions here because transmutability calls layout queries,
// which does not handle inference regions and doesn't particularly
@@ -301,21 +347,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(Unimplemented);
};
+ let dst = predicate.trait_ref.substs.type_at(0);
+ let src = predicate.trait_ref.substs.type_at(1);
+ debug!(?src, ?dst);
let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
let maybe_transmutable = transmute_env.is_transmutable(
obligation.cause.clone(),
- rustc_transmute::Types {
- dst: predicate.trait_ref.substs.type_at(0),
- src: predicate.trait_ref.substs.type_at(1),
- },
+ rustc_transmute::Types { dst, src },
predicate.trait_ref.substs.type_at(2),
assume,
);
- match maybe_transmutable {
- rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
- _ => Err(Unimplemented),
- }
+ let fully_flattened = match maybe_transmutable {
+ Answer::No(_) => Err(Unimplemented)?,
+ Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
+ Answer::Yes => vec![],
+ };
+
+ debug!(?fully_flattened);
+ Ok(fully_flattened)
}
/// This handles the case where an `auto trait Foo` impl is being used.
@@ -325,22 +375,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
fn confirm_auto_impl_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_auto_impl_candidate");
let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty());
- let types = self.constituent_types_for_ty(self_ty);
- self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)
+ let types = self.constituent_types_for_ty(self_ty)?;
+ Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types))
}
/// See `confirm_auto_impl_candidate`.
fn vtable_auto_impl(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
trait_def_id: DefId,
nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>,
- ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> {
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?nested, "vtable_auto_impl");
ensure_sufficient_stack(|| {
let cause = obligation.derived_cause(BuiltinDerivedObligation);
@@ -370,13 +420,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?obligations, "vtable_auto_impl");
- ImplSourceAutoImplData { trait_def_id, nested: obligations }
+ obligations
})
}
fn confirm_impl_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_def_id: DefId,
) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
debug!(?obligation, ?impl_def_id, "confirm_impl_candidate");
@@ -431,9 +481,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_object_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
index: usize,
- ) -> Result<ImplSourceObjectData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
debug!(?obligation, ?index, "confirm_object_candidate");
@@ -527,9 +577,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
substs.extend(trait_predicate.trait_ref.substs.iter());
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
smallvec::SmallVec::with_capacity(
- bound.0.kind().bound_vars().len() + defs.count(),
+ bound.skip_binder().kind().bound_vars().len() + defs.count(),
);
- bound_vars.extend(bound.0.kind().bound_vars().into_iter());
+ bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter());
InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param
.kind
{
@@ -537,7 +587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let kind = ty::BoundTyKind::Param(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Ty(kind);
bound_vars.push(bound_var);
- tcx.mk_bound(
+ Ty::new_bound(
+ tcx,
ty::INNERMOST,
ty::BoundTy {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
@@ -550,7 +601,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
let bound_var = ty::BoundVariableKind::Region(kind);
bound_vars.push(bound_var);
- tcx.mk_re_late_bound(
+ ty::Region::new_late_bound(
+ tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
@@ -562,11 +614,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
GenericParamDefKind::Const { .. } => {
let bound_var = ty::BoundVariableKind::Const;
bound_vars.push(bound_var);
- tcx.mk_const(
- ty::ConstKind::Bound(
- ty::INNERMOST,
- ty::BoundVar::from_usize(bound_vars.len() - 1),
- ),
+ ty::Const::new_bound(
+ tcx,
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
tcx.type_of(param.def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic"),
@@ -578,7 +629,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let assoc_ty_substs = tcx.mk_substs(&substs);
let bound =
bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs);
- tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars))
+ ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx)
};
let normalized_bound = normalize_with_depth_to(
self,
@@ -599,15 +650,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
(unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)),
);
- Ok(ImplSourceObjectData { upcast_trait_ref, vtable_base, nested })
+ Ok(ImplSourceObjectData { vtable_base, nested })
}
fn confirm_fn_pointer_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
is_const: bool,
- ) -> Result<ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
debug!(?obligation, "confirm_fn_pointer_candidate");
let tcx = self.tcx();
@@ -659,16 +709,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let tr = ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [output_ty]);
nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr));
- Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested })
+ Ok(nested)
}
fn confirm_trait_alias_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>> {
debug!(?obligation, "confirm_trait_alias_candidate");
- let alias_def_id = obligation.predicate.def_id();
let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
let trait_ref = predicate.trait_ref;
let trait_def_id = trait_ref.def_id;
@@ -685,14 +734,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?trait_def_id, ?trait_obligations, "trait alias obligations");
- ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations }
+ trait_obligations
}
fn confirm_generator_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
@@ -725,13 +773,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "generator candidate obligations");
- Ok(ImplSourceGeneratorData { generator_def_id, substs, nested })
+ Ok(nested)
}
fn confirm_future_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// Okay to skip binder because the substs on generator types never
// touch bound regions, they just capture the in-scope
// type/region parameters.
@@ -755,14 +803,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
debug!(?trait_ref, ?nested, "future candidate obligations");
- Ok(ImplSourceFutureData { generator_def_id, substs, nested })
+ Ok(nested)
}
#[instrument(skip(self), level = "debug")]
fn confirm_closure_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let kind = self
.tcx()
.fn_trait_kind_from_def_id(obligation.predicate.def_id())
@@ -781,15 +829,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations");
- // FIXME: Chalk
- if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Chalk {
- nested.push(obligation.with(
- self.tcx(),
- ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
- ));
- }
+ nested.push(obligation.with(
+ self.tcx(),
+ ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)),
+ ));
- Ok(ImplSourceClosureData { closure_def_id, substs, nested })
+ Ok(nested)
}
/// In the case of closure types and fn pointers,
@@ -820,7 +865,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(skip(self), level = "trace")]
fn confirm_poly_trait_refs(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
self_ty_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let obligation_trait_ref = obligation.predicate.to_poly_trait_ref();
@@ -855,10 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn confirm_trait_upcasting_unsize_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
idx: usize,
- ) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>>
- {
+ ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -903,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(ty::Binder::dummy),
);
let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
- let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_a);
+ let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a);
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
@@ -955,13 +999,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let vtable_vptr_slot =
prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap();
- Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested })
+ Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested })
}
fn confirm_builtin_unsize_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
- ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ obligation: &PolyTraitObligation<'tcx>,
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
let tcx = self.tcx();
// `assemble_candidates_for_unsizing` should ensure there are no late-bound
@@ -996,7 +1040,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.map(ty::Binder::dummy),
);
let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter);
- let source_trait = tcx.mk_dynamic(existential_predicates, r_b, dyn_a);
+ let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a);
// Require that the traits involved in this upcast are **equal**;
// only the **lifetime bound** is changed.
@@ -1054,12 +1098,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
obligation.cause.span,
[source],
);
- nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx)));
+ nested.push(predicate_to_obligation(tr.to_predicate(tcx)));
// If the type is `Foo + 'a`, ensure that the type
// being cast to `Foo + 'a` outlives `'a`:
let outlives = ty::OutlivesPredicate(source, r);
- nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate(tcx)));
+ nested.push(predicate_to_obligation(
+ ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx),
+ ));
}
// `[T; n]` -> `[T]`
@@ -1079,12 +1125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Err(Unimplemented);
}
- let tail_field = def
- .non_enum_variant()
- .fields
- .raw
- .last()
- .expect("expected unsized ADT to have a tail field");
+ let tail_field = def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
// Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
@@ -1112,7 +1153,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
let substs = tcx.mk_substs_from_iter(substs_a.iter().enumerate().map(|(i, k)| {
if unsizing_params.contains(i as u32) { substs_b[i] } else { k }
}));
- let new_struct = tcx.mk_adt(def, substs);
+ let new_struct = Ty::new_adt(tcx, def, substs);
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
@@ -1143,7 +1184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// Check that the source tuple with the target's
// last element is equal to the target.
let new_tuple =
- tcx.mk_tup_from_iter(a_mid.iter().copied().chain(iter::once(b_last)));
+ Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last)));
let InferOk { obligations, .. } = self
.infcx
.at(&obligation.cause, obligation.param_env)
@@ -1162,17 +1203,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
_ => bug!("source: {source}, target: {target}"),
};
- Ok(ImplSourceBuiltinData { nested })
+ Ok(nested)
}
fn confirm_const_destruct_candidate(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
impl_def_id: Option<DefId>,
- ) -> Result<ImplSourceConstDestructData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+ ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
// `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop`
if !obligation.is_const() {
- return Ok(ImplSourceConstDestructData { nested: vec![] });
+ return Ok(vec![]);
}
let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None);
@@ -1326,6 +1367,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- Ok(ImplSourceConstDestructData { nested })
+ Ok(nested)
}
}
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 3baf1c97c..7f31ab751 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -15,11 +15,12 @@ use super::util::closure_trait_ref_and_return_type;
use super::wf;
use super::{
ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation,
- ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, Selection, SelectionError,
- SelectionResult, TraitObligation, TraitQueryMode,
+ ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation,
+ Selection, SelectionError, SelectionResult, TraitQueryMode,
};
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
+use crate::solve::InferCtxtSelectExt;
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::project::try_normalize_with_depth_to;
use crate::traits::project::ProjectAndUnifyResult;
@@ -33,8 +34,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::infer::LateBoundRegionConversionTime;
-use rustc_infer::traits::TraitEngine;
-use rustc_infer::traits::TraitEngineExt;
+use rustc_infer::traits::TraitObligation;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -123,7 +123,7 @@ pub struct SelectionContext<'cx, 'tcx> {
// A stack that walks back up the stack frame.
struct TraitObligationStack<'prev, 'tcx> {
- obligation: &'prev TraitObligation<'tcx>,
+ obligation: &'prev PolyTraitObligation<'tcx>,
/// The trait predicate from `obligation` but "freshened" with the
/// selection-context's freshener. Used to check for recursion.
@@ -260,10 +260,14 @@ 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), ret)]
- pub fn select(
+ pub fn poly_select(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, Selection<'tcx>> {
+ if self.infcx.next_trait_solver() {
+ return self.infcx.select_in_new_trait_solver(obligation);
+ }
+
let candidate = match self.select_from_obligation(obligation) {
Err(SelectionError::Overflow(OverflowError::Canonical)) => {
// In standard mode, overflow must have been caught and reported
@@ -290,9 +294,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- pub(crate) fn select_from_obligation(
+ pub fn select(
&mut self,
obligation: &TraitObligation<'tcx>,
+ ) -> SelectionResult<'tcx, Selection<'tcx>> {
+ self.poly_select(&Obligation {
+ cause: obligation.cause.clone(),
+ param_env: obligation.param_env,
+ predicate: ty::Binder::dummy(obligation.predicate),
+ recursion_depth: obligation.recursion_depth,
+ })
+ }
+
+ fn select_from_obligation(
+ &mut self,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
debug_assert!(!obligation.predicate.has_escaping_bound_vars());
@@ -307,6 +323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
+ debug_assert!(!self.infcx.next_trait_solver());
// Watch out for overflow. This intentionally bypasses (and does
// not update) the cache.
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
@@ -365,7 +382,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
if !candidate_set.ambiguous && no_candidates_apply {
- let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
+ let trait_ref = self.infcx.resolve_vars_if_possible(
+ stack.obligation.predicate.skip_binder().trait_ref,
+ );
if !trait_ref.references_error() {
let self_ty = trait_ref.self_ty();
let (trait_desc, self_desc) = with_no_trimmed_paths!({
@@ -516,37 +535,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// The result is "true" if the obligation *may* hold and "false" if
// we can be sure it does not.
- /// Evaluates whether the obligation `obligation` can be satisfied (by any means).
- pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool {
- 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.
- assert!(self.query_mode == TraitQueryMode::Standard);
-
- self.evaluate_root_obligation(obligation)
- .expect("Overflow should be caught earlier in standard query mode")
- .may_apply()
- }
-
/// Evaluates whether the obligation `obligation` can be satisfied
/// and returns an `EvaluationResult`. This is meant for the
/// *initial* call.
+ ///
+ /// Do not use this directly, use `infcx.evaluate_obligation` instead.
pub fn evaluate_root_obligation(
&mut self,
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
+ debug_assert!(!self.infcx.next_trait_solver());
self.evaluation_probe(|this| {
let goal =
this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
- let mut result = if this.tcx().trait_solver_next() {
- this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])?
- } else {
- this.evaluate_predicate_recursively(
- TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
- obligation.clone(),
- )?
- };
+ let mut result = this.evaluate_predicate_recursively(
+ TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+ obligation.clone(),
+ )?;
// If the predicate has done any inference, then downgrade the
// result to ambiguous.
if this.infcx.shallow_resolve(goal) != goal {
@@ -561,9 +566,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
) -> Result<EvaluationResult, OverflowError> {
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
+ let outer_universe = self.infcx.universe();
let result = op(self)?;
- match self.infcx.leak_check(true, snapshot) {
+ match self.infcx.leak_check(outer_universe, Some(snapshot)) {
Ok(()) => {}
Err(_) => return Ok(EvaluatedToErr),
}
@@ -572,9 +578,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
}
- match self.infcx.region_constraints_added_in_snapshot(snapshot) {
- None => Ok(result),
- Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
+ if self.infcx.region_constraints_added_in_snapshot(snapshot) {
+ Ok(result.max(EvaluatedToOkModuloRegions))
+ } else {
+ Ok(result)
}
})
}
@@ -591,42 +598,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
where
I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
{
- if self.tcx().trait_solver_next() {
- self.evaluate_predicates_recursively_in_new_solver(predicates)
- } else {
- let mut result = EvaluatedToOk;
- for mut obligation in predicates {
- obligation.set_depth_from_parent(stack.depth());
- let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
- if let EvaluatedToErr = eval {
- // fast-path - EvaluatedToErr is the top of the lattice,
- // so we don't need to look on the other predicates.
- return Ok(EvaluatedToErr);
- } else {
- result = cmp::max(result, eval);
- }
+ let mut result = EvaluatedToOk;
+ for mut obligation in predicates {
+ obligation.set_depth_from_parent(stack.depth());
+ let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
+ if let EvaluatedToErr = eval {
+ // fast-path - EvaluatedToErr is the top of the lattice,
+ // so we don't need to look on the other predicates.
+ return Ok(EvaluatedToErr);
+ } else {
+ result = cmp::max(result, eval);
}
- Ok(result)
}
- }
-
- /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled
- fn evaluate_predicates_recursively_in_new_solver(
- &mut self,
- predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
- ) -> Result<EvaluationResult, OverflowError> {
- let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
- fulfill_cx.register_predicate_obligations(self.infcx, predicates);
- // True errors
- // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK?
- if !fulfill_cx.select_where_possible(self.infcx).is_empty() {
- return Ok(EvaluatedToErr);
- }
- if !fulfill_cx.select_all_or_error(self.infcx).is_empty() {
- return Ok(EvaluatedToAmbig);
- }
- // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot
- Ok(EvaluatedToOk)
+ Ok(result)
}
#[instrument(
@@ -640,7 +624,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
previous_stack: TraitObligationStackList<'o, 'tcx>,
obligation: PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
- // `previous_stack` stores a `TraitObligation`, while `obligation` is
+ debug_assert!(!self.infcx.next_trait_solver());
+ // `previous_stack` stores a `PolyTraitObligation`, while `obligation` is
// a `PredicateObligation`. These are distinct types, so we can't
// use any `Option` combinator method that would force them to be
// the same.
@@ -652,7 +637,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
ensure_sufficient_stack(|| {
let bound_predicate = obligation.predicate.kind();
match bound_predicate.skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
let t = bound_predicate.rebind(t);
debug_assert!(!t.has_escaping_bound_vars());
let obligation = obligation.with(self.tcx(), t);
@@ -683,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
// So, there is a bit going on here. First, `WellFormed` predicates
// are coinductive, like trait predicates with auto traits.
// This means that we need to detect if we have recursively
@@ -769,7 +754,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(pred)) => {
// A global type with no free lifetimes or generic parameters
// outlives anything.
if pred.0.has_free_regions()
@@ -783,7 +768,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {
// We do not consider region relationships when evaluating trait matches.
Ok(EvaluatedToOkModuloRegions)
}
@@ -796,7 +781,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::Clause(ty::Clause::Projection(data)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
let data = bound_predicate.rebind(data);
let project_obligation = obligation.with(self.tcx(), data);
match project::poly_project_and_unify_type(self, &project_obligation) {
@@ -871,7 +856,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
- ty::PredicateKind::ConstEvaluatable(uv) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => {
match const_evaluatable::is_const_evaluatable(
self.infcx,
uv,
@@ -901,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
);
use rustc_hir::def::DefKind;
- use ty::ConstKind::Unevaluated;
+ use ty::Unevaluated;
match (c1.kind(), c2.kind()) {
(Unevaluated(a), Unevaluated(b))
if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst =>
@@ -976,14 +961,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}
}
}
- ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("TypeWellFormedFromEnv is only used for chalk")
- }
ty::PredicateKind::AliasRelate(..) => {
bug!("AliasRelate is only used for new solver")
}
ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
match self.infcx.at(&obligation.cause, obligation.param_env).eq(
DefineOpaqueTypes::No,
ct.ty(),
@@ -1004,7 +986,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn evaluate_trait_predicate_recursively<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
- mut obligation: TraitObligation<'tcx>,
+ mut obligation: PolyTraitObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
if !self.is_intercrate()
&& obligation.is_global()
@@ -1186,6 +1168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
&mut self,
stack: &TraitObligationStack<'o, 'tcx>,
) -> Result<EvaluationResult, OverflowError> {
+ debug_assert!(!self.infcx.next_trait_solver());
// In intercrate mode, whenever any of the generics are unbound,
// there can always be an impl. Even if there are no impls in
// this crate, perhaps the type would be unified with
@@ -1409,7 +1392,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn filter_impls(
&mut self,
candidates: Vec<SelectionCandidate<'tcx>>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Vec<SelectionCandidate<'tcx>> {
trace!("{candidates:#?}");
let tcx = self.tcx();
@@ -1472,7 +1455,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
fn filter_reservation_impls(
&mut self,
candidate: SelectionCandidate<'tcx>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
let tcx = self.tcx();
// Treat reservation impls as ambiguity.
@@ -1644,7 +1627,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn match_projection_obligation_against_definition_bounds(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> {
let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate);
let placeholder_trait_predicate =
@@ -1677,9 +1660,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
.enumerate()
.filter_map(|(idx, bound)| {
let bound_predicate = bound.kind();
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
- bound_predicate.skip_binder()
- {
+ if let ty::ClauseKind::Trait(pred) = bound_predicate.skip_binder() {
let bound = bound_predicate.rebind(pred.trait_ref);
if self.infcx.probe(|_| {
match self.match_normalize_trait_ref(
@@ -1709,7 +1690,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
/// variables or placeholders, the normalized bound is returned.
fn match_normalize_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
trait_bound: ty::PolyTraitRef<'tcx>,
placeholder_trait_ref: ty::TraitRef<'tcx>,
) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> {
@@ -2110,7 +2091,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
impl<'tcx> SelectionContext<'_, 'tcx> {
fn sized_conditions(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
use self::BuiltinImplConditions::{Ambiguous, None, Where};
@@ -2149,13 +2130,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
ty::Adt(def, substs) => {
let sized_crit = def.sized_constraint(self.tcx());
// (*) binder moved here
- Where(obligation.predicate.rebind({
- sized_crit
- .0
- .iter()
- .map(|ty| sized_crit.rebind(*ty).subst(self.tcx(), substs))
- .collect()
- }))
+ Where(
+ obligation
+ .predicate
+ .rebind(sized_crit.subst_iter_copied(self.tcx(), substs).collect()),
+ )
}
ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
@@ -2172,7 +2151,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn copy_clone_conditions(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> BuiltinImplConditions<'tcx> {
// NOTE: binder moved to (*)
let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty());
@@ -2305,8 +2284,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn constituent_types_for_ty(
&self,
t: ty::Binder<'tcx, Ty<'tcx>>,
- ) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
- match *t.skip_binder().kind() {
+ ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> {
+ Ok(match *t.skip_binder().kind() {
ty::Uint(_)
| ty::Int(_)
| ty::Bool
@@ -2319,13 +2298,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
| ty::Char => ty::Binder::dummy(Vec::new()),
// Treat this like `struct str([u8]);`
- ty::Str => ty::Binder::dummy(vec![self.tcx().mk_slice(self.tcx().types.u8)]),
+ ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]),
ty::Placeholder(..)
| ty::Dynamic(..)
| ty::Param(..)
| ty::Foreign(..)
- | ty::Alias(ty::Projection | ty::Inherent, ..)
+ | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..)
| ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
bug!("asked to assemble constituent types of unexpected type: {:?}", t);
@@ -2370,12 +2349,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ let ty = self.tcx().type_of(def_id);
+ if ty.skip_binder().references_error() {
+ return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+ }
// We can resolve the `impl Trait` to its concrete type,
// which enforces a DAG between the functions requiring
// the auto trait bounds in question.
- t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)])
+ t.rebind(vec![ty.subst(self.tcx(), substs)])
}
- }
+ })
}
fn collect_predicates_for_types(
@@ -2444,7 +2427,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn rematch_impl(
&mut self,
impl_def_id: DefId,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Normalized<'tcx, SubstsRef<'tcx>> {
let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
@@ -2465,7 +2448,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
),
);
let value = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id);
- let err = self.tcx().ty_error(guar);
+ let err = Ty::new_error(self.tcx(), guar);
let value = value.fold_with(&mut BottomUpFolder {
tcx: self.tcx(),
ty_op: |_| err,
@@ -2482,7 +2465,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
&mut self,
impl_def_id: DefId,
impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> {
let placeholder_obligation =
self.infcx.instantiate_binder_with_placeholders(obligation.predicate);
@@ -2540,7 +2523,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
/// result from the normalization.
fn match_where_clause_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
where_clause_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
self.match_poly_trait_ref(obligation, where_clause_trait_ref)
@@ -2551,7 +2534,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn match_poly_trait_ref(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
poly_trait_ref: ty::PolyTraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> {
self.infcx
@@ -2577,7 +2560,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
fn push_stack<'o>(
&mut self,
previous_stack: TraitObligationStackList<'o, 'tcx>,
- obligation: &'o TraitObligation<'tcx>,
+ obligation: &'o PolyTraitObligation<'tcx>,
) -> TraitObligationStack<'o, 'tcx> {
let fresh_trait_pred = obligation.predicate.fold_with(&mut self.freshener);
@@ -2596,7 +2579,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn closure_trait_ref_unnormalized(
&mut self,
- obligation: &TraitObligation<'tcx>,
+ obligation: &PolyTraitObligation<'tcx>,
substs: SubstsRef<'tcx>,
) -> ty::PolyTraitRef<'tcx> {
let closure_sig = substs.as_closure().sig();
@@ -2670,7 +2653,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
}))
})
};
- let predicate = normalize_with_depth_to(
+ let clause = normalize_with_depth_to(
self,
param_env,
cause.clone(),
@@ -2678,7 +2661,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
predicate,
&mut obligations,
);
- obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
+ obligations.push(Obligation {
+ cause,
+ recursion_depth,
+ param_env,
+ predicate: clause.as_predicate(),
+ });
}
obligations
@@ -3029,7 +3017,7 @@ fn bind_generator_hidden_types_above<'tcx>(
kind: ty::BrAnon(None),
};
counter += 1;
- tcx.mk_re_late_bound(current_depth, br)
+ ty::Region::new_late_bound(tcx, current_depth, br)
}
r => bug!("unexpected region: {r:?}"),
})
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 9a4b72013..96f128741 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -238,7 +238,7 @@ fn fulfill_implication<'tcx>(
// Needs to be `in_snapshot` because this function is used to rebase
// substitutions, which may happen inside of a select within a probe.
- let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let ocx = ObligationCtxt::new(infcx);
// attempt to prove all of the predicates for impl2 given those for impl1
// (which are packed up in penv)
ocx.register_obligations(obligations.chain(more_obligations));
@@ -508,21 +508,14 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
for (mut p, _) in predicates {
- if let Some(poly_trait_ref) = p.to_opt_poly_trait_pred() {
+ if let Some(poly_trait_ref) = p.as_trait_clause() {
if Some(poly_trait_ref.def_id()) == sized_trait {
types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
continue;
}
if ty::BoundConstness::ConstIfConst == poly_trait_ref.skip_binder().constness {
- let new_trait_pred = poly_trait_ref.map_bound(|mut trait_pred| {
- trait_pred.constness = ty::BoundConstness::NotConst;
- trait_pred
- });
-
- p = tcx.mk_predicate(
- new_trait_pred.map_bound(|p| ty::PredicateKind::Clause(ty::Clause::Trait(p))),
- )
+ p = p.without_const(tcx);
}
}
pretty_predicates.push(p.to_string());
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index e38ae9381..420f8c5dc 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -34,24 +34,7 @@ pub fn search_for_structural_match_violation<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
- ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
- .break_value()
-}
-
-/// This method traverses the structure of `ty`, trying to find any
-/// types that are not allowed to be used in a const generic.
-///
-/// This is either because the type does not implement `StructuralEq`
-/// and `StructuralPartialEq`, or because the type is intentionally
-/// not supported in const generics (such as floats and raw pointers,
-/// which are allowed in match blocks).
-pub fn search_for_adt_const_param_violation<'tcx>(
- span: Span,
- tcx: TyCtxt<'tcx>,
- ty: Ty<'tcx>,
-) -> Option<Ty<'tcx>> {
- ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
- .break_value()
+ ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
}
/// This implements the traversal over the structure of a given type to try to
@@ -65,11 +48,6 @@ struct Search<'tcx> {
/// Tracks ADTs previously encountered during search, so that
/// we will not recur on them again.
seen: FxHashSet<hir::def_id::DefId>,
-
- // Additionally deny things that have been allowed in patterns,
- // but are not allowed in adt const params, such as floats and
- // fn ptrs.
- adt_const_param: bool,
}
impl<'tcx> Search<'tcx> {
@@ -124,41 +102,29 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
}
ty::FnPtr(..) => {
- if !self.adt_const_param {
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ return ControlFlow::Continue(());
}
ty::RawPtr(..) => {
- if !self.adt_const_param {
- // structural-match ignores substructure of
- // `*const _`/`*mut _`, so skip `super_visit_with`.
- //
- // For example, if you have:
- // ```
- // struct NonStructural;
- // #[derive(PartialEq, Eq)]
- // struct T(*const NonStructural);
- // const C: T = T(std::ptr::null());
- // ```
- //
- // 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.
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ // structural-match ignores substructure of
+ // `*const _`/`*mut _`, so skip `super_visit_with`.
+ //
+ // For example, if you have:
+ // ```
+ // struct NonStructural;
+ // #[derive(PartialEq, Eq)]
+ // struct T(*const NonStructural);
+ // const C: T = T(std::ptr::null());
+ // ```
+ //
+ // 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.
+ return ControlFlow::Continue(());
}
ty::Float(_) => {
- if !self.adt_const_param {
- return ControlFlow::Continue(());
- } else {
- return ControlFlow::Break(ty);
- }
+ return ControlFlow::Continue(());
}
ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index af8dd0da5..84746eba3 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
assert!(!ty.is_ty_var(), "should have resolved vars before calling");
- if self.infcx.tcx.trait_solver_next() {
+ if self.infcx.next_trait_solver() {
while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::NormalizeProjectionType,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 82f3df401..302b6cacf 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -41,7 +41,12 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
/// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
/// trait aliases.
- pub fn label_with_exp_info(&self, diag: &mut Diagnostic, top_label: &str, use_desc: &str) {
+ pub fn label_with_exp_info(
+ &self,
+ diag: &mut Diagnostic,
+ top_label: &'static str,
+ use_desc: &str,
+ ) {
diag.span_label(self.top().1, top_label);
if self.path.len() > 1 {
for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
@@ -120,7 +125,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
pred.subst_supertrait(tcx, &trait_ref)
- .to_opt_poly_trait_pred()
+ .as_trait_clause()
.map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
});
debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>());
@@ -177,7 +182,7 @@ impl Iterator for SupertraitDefIds<'_> {
predicates
.predicates
.iter()
- .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred())
+ .filter_map(|(pred, _)| pred.as_trait_clause())
.map(|trait_ref| trait_ref.def_id())
.filter(|&super_def_id| visited.insert(super_def_id)),
);
@@ -238,12 +243,12 @@ pub fn upcast_choices<'tcx>(
/// `object.upcast_trait_ref`) within the vtable for `object`.
pub fn get_vtable_index_of_object_method<'tcx, N>(
tcx: TyCtxt<'tcx>,
- object: &super::ImplSourceObjectData<'tcx, N>,
+ object: &super::ImplSourceObjectData<N>,
method_def_id: DefId,
) -> Option<usize> {
// Count number of methods preceding the one we are selecting and
// add them to the total offset.
- tcx.own_existential_vtable_entries(object.upcast_trait_ref.def_id())
+ tcx.own_existential_vtable_entries(tcx.parent(method_def_id))
.iter()
.copied()
.position(|def_id| def_id == method_def_id)
@@ -260,7 +265,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
assert!(!self_ty.has_escaping_bound_vars());
let arguments_tuple = match tuple_arguments {
TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
- TupleArgumentsFlag::Yes => tcx.mk_tup(sig.skip_binder().inputs()),
+ TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
};
let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]);
sig.map_bound(|sig| (trait_ref, sig.output()))
@@ -290,10 +295,51 @@ pub fn future_trait_ref_and_outputs<'tcx>(
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
assoc_item.defaultness(tcx).is_final()
- && tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final()
+ && tcx.defaultness(assoc_item.container_id(tcx)).is_final()
}
pub enum TupleArgumentsFlag {
Yes,
No,
}
+
+// Verify that the trait item and its implementation have compatible substs lists
+pub fn check_substs_compatible<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ assoc_item: ty::AssocItem,
+ substs: ty::SubstsRef<'tcx>,
+) -> bool {
+ fn check_substs_compatible_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: &'tcx ty::Generics,
+ args: &'tcx [ty::GenericArg<'tcx>],
+ ) -> bool {
+ if generics.count() != args.len() {
+ return false;
+ }
+
+ let (parent_args, own_args) = args.split_at(generics.parent_count);
+
+ if let Some(parent) = generics.parent
+ && let parent_generics = tcx.generics_of(parent)
+ && !check_substs_compatible_inner(tcx, parent_generics, parent_args) {
+ return false;
+ }
+
+ for (param, arg) in std::iter::zip(&generics.params, own_args) {
+ match (&param.kind, arg.unpack()) {
+ (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_))
+ | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_))
+ | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {}
+ _ => return false,
+ }
+ }
+
+ true
+ }
+
+ let generics = tcx.generics_of(assoc_item.def_id);
+ // Chop off any additional substs (RPITIT) substs
+ let substs = &substs[0..generics.count().min(substs.len())];
+ check_substs_compatible_inner(tcx, generics, substs)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index cc674ceee..1f83f1f44 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -15,13 +15,13 @@ use std::fmt::Debug;
use std::ops::ControlFlow;
#[derive(Clone, Debug)]
-pub(super) enum VtblSegment<'tcx> {
+pub enum VtblSegment<'tcx> {
MetadataDSA,
TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
}
/// Prepare the segments for a vtable
-pub(super) fn prepare_vtable_segments<'tcx, T>(
+pub fn prepare_vtable_segments<'tcx, T>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
@@ -115,7 +115,7 @@ pub(super) fn prepare_vtable_segments<'tcx, T>(
.predicates
.into_iter()
.filter_map(move |(pred, _)| {
- pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred()
+ pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
});
'diving_in_skip_visited_traits: loop {
@@ -362,7 +362,7 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]);
- match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) {
+ match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) {
Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => {
implsrc_traitcasting.vtable_vptr_slot
}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 086ab32b5..d81722ce2 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -77,12 +77,19 @@ pub fn unnormalized_obligations<'tcx>(
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+ debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
+
+ // However, if `arg` IS an unresolved inference variable, returns `None`,
+ // because we are not able to make any progress at all. This is to prevent
+ // "livelock" where we say "$0 is WF if $0 is WF".
+ if arg.is_non_region_infer() {
+ return None;
+ }
+
if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
return Some(vec![]);
}
- debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
-
let mut wf = WfPredicates {
infcx,
param_env,
@@ -142,29 +149,32 @@ pub fn predicate_obligations<'tcx>(
// It's ok to skip the binder here because wf code is prepared for it
match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
wf.compute_trait_pred(&t, Elaborate::None);
}
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {}
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, _reg))) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {}
+ ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+ ty,
+ _reg,
+ ))) => {
wf.compute(ty.into());
}
- ty::PredicateKind::Clause(ty::Clause::Projection(t)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(t)) => {
wf.compute_projection(t.projection_ty);
wf.compute(match t.term.unpack() {
ty::TermKind::Ty(ty) => ty.into(),
ty::TermKind::Const(c) => c.into(),
})
}
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
wf.compute(ct.into());
wf.compute(ty.into());
}
- ty::PredicateKind::WellFormed(arg) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
wf.compute(arg);
}
- ty::PredicateKind::ConstEvaluatable(ct) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => {
wf.compute(ct.into());
}
@@ -174,8 +184,7 @@ pub fn predicate_obligations<'tcx>(
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+ | ty::PredicateKind::AliasRelate(..) => {
bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}")
}
}
@@ -247,7 +256,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
// It is fine to skip the binder as we don't care about regions here.
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) => {
// The obligation comes not from the current `impl` nor the `trait` being implemented,
// but rather from a "second order" obligation, where an associated type has a
// projection coming from another associated type. See
@@ -264,7 +273,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>(
cause.span = impl_item_span;
}
}
- ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => {
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
// An associated item obligation born out of the `trait` failed to be met. An example
// can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`.
debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred);
@@ -293,6 +302,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> {
+ // Do not normalize `wf` obligations with the new solver.
+ //
+ // The current deep normalization routine with the new solver does not
+ // handle ambiguity and the new solver correctly deals with unnnormalized goals.
+ // If the user relies on normalized types, e.g. for `fn implied_outlives_bounds`,
+ // it is their responsibility to normalize while avoiding ambiguity.
+ if infcx.next_trait_solver() {
+ return self.out;
+ }
+
let cause = self.cause(traits::WellFormed(None));
let param_env = self.param_env;
let mut obligations = Vec::with_capacity(self.out.len());
@@ -386,7 +405,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ arg,
+ ))),
)
}),
);
@@ -478,7 +499,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause.clone(),
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ arg,
+ ))),
)
}),
);
@@ -521,8 +544,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
let obligations = self.nominal_obligations(uv.def, uv.substs);
self.out.extend(obligations);
- let predicate =
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::ConstEvaluatable(ct),
+ ));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@@ -541,7 +565,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::WellFormed(ct.into()),
+ )),
));
}
ty::ConstKind::Expr(_) => {
@@ -552,8 +578,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// the future we may allow directly lowering to `ConstKind::Expr` in which case
// we would not be proving bounds we should.
- let predicate =
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+ let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::ConstEvaluatable(ct),
+ ));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
@@ -654,9 +681,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(rty, r),
- ))),
+ ty::Binder::dummy(ty::PredicateKind::Clause(
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)),
+ )),
));
}
}
@@ -731,6 +758,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
}
}
+ ty::Alias(ty::Weak, ty::AliasTy { def_id, substs, .. }) => {
+ let obligations = self.nominal_obligations(def_id, substs);
+ self.out.extend(obligations);
+ }
+
ty::Dynamic(data, r, _) => {
// WfObject
//
@@ -779,7 +811,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
cause,
self.recursion_depth,
param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())),
+ ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
+ ty.into(),
+ ))),
));
}
}
@@ -918,7 +952,7 @@ pub fn object_region_bounds<'tcx>(
// Since we don't actually *know* the self type for an object,
// this "open(err)" serves as a kind of dummy standin -- basically
// a placeholder type.
- let open_ty = tcx.mk_fresh_ty(0);
+ let open_ty = Ty::new_fresh(tcx, 0);
let predicates = existential_predicates.iter().filter_map(|predicate| {
if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() {
@@ -951,7 +985,7 @@ pub fn object_region_bounds<'tcx>(
pub(crate) fn required_region_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
erased_self_ty: Ty<'tcx>,
- predicates: impl Iterator<Item = ty::Predicate<'tcx>>,
+ predicates: impl Iterator<Item = ty::Clause<'tcx>>,
) -> Vec<ty::Region<'tcx>> {
assert!(!erased_self_ty.has_escaping_bound_vars());
@@ -959,24 +993,7 @@ pub(crate) fn required_region_bounds<'tcx>(
.filter_map(|pred| {
debug!(?pred);
match pred.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::WellFormed(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
- ref t,
- ref r,
- ))) => {
+ ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => {
// Search for a bound of the form `erased_self_ty
// : 'a`, but be wary of something like `for<'a>
// erased_self_ty : 'a` (we interpret a
@@ -992,6 +1009,12 @@ pub(crate) fn required_region_bounds<'tcx>(
None
}
}
+ ty::ClauseKind::Trait(_)
+ | ty::ClauseKind::RegionOutlives(_)
+ | ty::ClauseKind::Projection(_)
+ | ty::ClauseKind::ConstArgHasType(_, _)
+ | ty::ClauseKind::WellFormed(_)
+ | ty::ClauseKind::ConstEvaluatable(_) => None,
}
})
.collect()
diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml
index dfd6fbff7..37e00c0e4 100644
--- a/compiler/rustc_traits/Cargo.toml
+++ b/compiler/rustc_traits/Cargo.toml
@@ -11,9 +11,6 @@ rustc_hir = { path = "../rustc_hir" }
rustc_ast = { path = "../rustc_ast" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
-chalk-ir = "0.87.0"
-chalk-engine = "0.87.0"
-chalk-solve = "0.87.0"
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs
deleted file mode 100644
index c319b2e31..000000000
--- a/compiler/rustc_traits/src/chalk/db.rs
+++ /dev/null
@@ -1,792 +0,0 @@
-//! Provides the `RustIrDatabase` implementation for `chalk-solve`
-//!
-//! The purpose of the `chalk_solve::RustIrDatabase` is to get data about
-//! specific types, such as bounds, where clauses, or fields. This file contains
-//! the minimal logic to assemble the types for `chalk-solve` by calling out to
-//! either the `TyCtxt` (for information about types) or
-//! `crate::chalk::lowering` (to lower rustc types into Chalk types).
-
-use rustc_middle::traits::ChalkRustInterner as RustInterner;
-use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
-use rustc_middle::ty::{InternalSubsts, SubstsRef};
-use rustc_target::abi::{Integer, IntegerType};
-
-use rustc_ast::ast;
-
-use rustc_hir::def_id::DefId;
-
-use rustc_span::symbol::sym;
-
-use std::fmt;
-use std::sync::Arc;
-
-use crate::chalk::lowering::LowerInto;
-
-pub struct RustIrDatabase<'tcx> {
- pub(crate) interner: RustInterner<'tcx>,
-}
-
-impl fmt::Debug for RustIrDatabase<'_> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "RustIrDatabase")
- }
-}
-
-impl<'tcx> RustIrDatabase<'tcx> {
- fn where_clauses_for(
- &self,
- def_id: DefId,
- bound_vars: SubstsRef<'tcx>,
- ) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
- self.interner
- .tcx
- .predicates_defined_on(def_id)
- .instantiate_own(self.interner.tcx, bound_vars)
- .filter_map(|(wc, _)| LowerInto::lower_into(wc, self.interner))
- .collect()
- }
-
- fn bounds_for<T>(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec<T>
- where
- ty::Predicate<'tcx>: LowerInto<'tcx, std::option::Option<T>>,
- {
- self.interner
- .tcx
- .explicit_item_bounds(def_id)
- .subst_iter_copied(self.interner.tcx, &bound_vars)
- .filter_map(|(bound, _)| LowerInto::<Option<_>>::lower_into(bound, self.interner))
- .collect()
- }
-}
-
-impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> {
- fn interner(&self) -> RustInterner<'tcx> {
- self.interner
- }
-
- fn associated_ty_data(
- &self,
- assoc_type_id: chalk_ir::AssocTypeId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::AssociatedTyDatum<RustInterner<'tcx>>> {
- let def_id = assoc_type_id.0;
- let assoc_item = self.interner.tcx.associated_item(def_id);
- let Some(trait_def_id) = assoc_item.trait_container(self.interner.tcx) else {
- unimplemented!("Not possible??");
- };
- match assoc_item.kind {
- AssocKind::Type => {}
- _ => unimplemented!("Not possible??"),
- }
- let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
- let binders = binders_for(self.interner, bound_vars);
-
- 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),
- id: assoc_type_id,
- name: (),
- binders: chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::AssociatedTyDatumBound { bounds, where_clauses },
- ),
- })
- }
-
- fn trait_datum(
- &self,
- trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::TraitDatum<RustInterner<'tcx>>> {
- use chalk_solve::rust_ir::WellKnownTrait::*;
-
- let def_id = trait_id.0;
- let trait_def = self.interner.tcx.trait_def(def_id);
-
- let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
- let binders = binders_for(self.interner, bound_vars);
-
- let where_clauses = self.where_clauses_for(def_id, bound_vars);
-
- let associated_ty_ids: Vec<_> = self
- .interner
- .tcx
- .associated_items(def_id)
- .in_definition_order()
- .filter(|i| i.kind == AssocKind::Type)
- .map(|i| chalk_ir::AssocTypeId(i.def_id))
- .collect();
-
- let lang_items = self.interner.tcx.lang_items();
- let well_known = if lang_items.sized_trait() == Some(def_id) {
- Some(Sized)
- } else if lang_items.copy_trait() == Some(def_id) {
- Some(Copy)
- } else if lang_items.clone_trait() == Some(def_id) {
- Some(Clone)
- } else if lang_items.drop_trait() == Some(def_id) {
- Some(Drop)
- } else if lang_items.fn_trait() == Some(def_id) {
- Some(Fn)
- } else if lang_items.fn_once_trait() == Some(def_id) {
- Some(FnOnce)
- } else if lang_items.fn_mut_trait() == Some(def_id) {
- Some(FnMut)
- } else if lang_items.unsize_trait() == Some(def_id) {
- Some(Unsize)
- } else if lang_items.unpin_trait() == Some(def_id) {
- Some(Unpin)
- } else if lang_items.coerce_unsized_trait() == Some(def_id) {
- Some(CoerceUnsized)
- } else if lang_items.dispatch_from_dyn_trait() == Some(def_id) {
- Some(DispatchFromDyn)
- } else if lang_items.tuple_trait() == Some(def_id) {
- Some(Tuple)
- } else {
- None
- };
- Arc::new(chalk_solve::rust_ir::TraitDatum {
- id: trait_id,
- binders: chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::TraitDatumBound { where_clauses },
- ),
- flags: chalk_solve::rust_ir::TraitFlags {
- auto: trait_def.has_auto_impl,
- marker: trait_def.is_marker,
- upstream: !def_id.is_local(),
- fundamental: self.interner.tcx.has_attr(def_id, sym::fundamental),
- non_enumerable: true,
- coinductive: false,
- },
- associated_ty_ids,
- well_known,
- })
- }
-
- fn adt_datum(
- &self,
- adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::AdtDatum<RustInterner<'tcx>>> {
- let adt_def = adt_id.0;
-
- let bound_vars = bound_vars_for_item(self.interner.tcx, adt_def.did());
- let binders = binders_for(self.interner, bound_vars);
-
- let where_clauses = self.where_clauses_for(adt_def.did(), bound_vars);
-
- let variants: Vec<_> = adt_def
- .variants()
- .iter()
- .map(|variant| chalk_solve::rust_ir::AdtVariantDatum {
- fields: variant
- .fields
- .iter()
- .map(|field| field.ty(self.interner.tcx, bound_vars).lower_into(self.interner))
- .collect(),
- })
- .collect();
- Arc::new(chalk_solve::rust_ir::AdtDatum {
- id: adt_id,
- binders: chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::AdtDatumBound { variants, where_clauses },
- ),
- flags: chalk_solve::rust_ir::AdtFlags {
- upstream: !adt_def.did().is_local(),
- fundamental: adt_def.is_fundamental(),
- phantom_data: adt_def.is_phantom_data(),
- },
- kind: match adt_def.adt_kind() {
- ty::AdtKind::Struct => chalk_solve::rust_ir::AdtKind::Struct,
- ty::AdtKind::Union => chalk_solve::rust_ir::AdtKind::Union,
- ty::AdtKind::Enum => chalk_solve::rust_ir::AdtKind::Enum,
- },
- })
- }
-
- fn adt_repr(
- &self,
- adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::AdtRepr<RustInterner<'tcx>>> {
- let adt_def = adt_id.0;
- let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)).intern(self.interner);
- let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i)).intern(self.interner);
- Arc::new(chalk_solve::rust_ir::AdtRepr {
- c: adt_def.repr().c(),
- packed: adt_def.repr().packed(),
- int: adt_def.repr().int.map(|i| match i {
- IntegerType::Pointer(true) => int(chalk_ir::IntTy::Isize),
- IntegerType::Pointer(false) => uint(chalk_ir::UintTy::Usize),
- IntegerType::Fixed(i, true) => match i {
- Integer::I8 => int(chalk_ir::IntTy::I8),
- Integer::I16 => int(chalk_ir::IntTy::I16),
- Integer::I32 => int(chalk_ir::IntTy::I32),
- Integer::I64 => int(chalk_ir::IntTy::I64),
- Integer::I128 => int(chalk_ir::IntTy::I128),
- },
- IntegerType::Fixed(i, false) => match i {
- Integer::I8 => uint(chalk_ir::UintTy::U8),
- Integer::I16 => uint(chalk_ir::UintTy::U16),
- Integer::I32 => uint(chalk_ir::UintTy::U32),
- Integer::I64 => uint(chalk_ir::UintTy::U64),
- Integer::I128 => uint(chalk_ir::UintTy::U128),
- },
- }),
- })
- }
-
- fn adt_size_align(
- &self,
- adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::AdtSizeAlign> {
- let tcx = self.interner.tcx;
- let did = adt_id.0.did();
-
- // Grab the ADT and the param we might need to calculate its layout
- let param_env = tcx.param_env(did);
- let adt_ty = tcx.type_of(did).subst_identity();
-
- // The ADT is a 1-zst if it's a ZST and its alignment is 1.
- // Mark the ADT as _not_ a 1-zst if there was a layout error.
- let one_zst = if let Ok(layout) = tcx.layout_of(param_env.and(adt_ty)) {
- layout.is_zst() && layout.align.abi.bytes() == 1
- } else {
- false
- };
-
- Arc::new(chalk_solve::rust_ir::AdtSizeAlign::from_one_zst(one_zst))
- }
-
- fn fn_def_datum(
- &self,
- fn_def_id: chalk_ir::FnDefId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::FnDefDatum<RustInterner<'tcx>>> {
- let def_id = fn_def_id.0;
- let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
- let binders = binders_for(self.interner, bound_vars);
-
- let where_clauses = self.where_clauses_for(def_id, bound_vars);
-
- let sig = self.interner.tcx.fn_sig(def_id);
- let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars(
- self.interner,
- self.interner.tcx,
- sig.map_bound(|s| s.inputs_and_output()).subst(self.interner.tcx, bound_vars),
- );
-
- let argument_types = inputs_and_output[..inputs_and_output.len() - 1]
- .iter()
- .map(|t| sig.rebind(*t).subst(self.interner.tcx, &bound_vars).lower_into(self.interner))
- .collect();
-
- let return_type = sig
- .rebind(inputs_and_output[inputs_and_output.len() - 1])
- .subst(self.interner.tcx, &bound_vars)
- .lower_into(self.interner);
-
- let bound = chalk_solve::rust_ir::FnDefDatumBound {
- inputs_and_output: chalk_ir::Binders::new(
- iobinders,
- chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type },
- ),
- where_clauses,
- };
- Arc::new(chalk_solve::rust_ir::FnDefDatum {
- id: fn_def_id,
- sig: sig.0.lower_into(self.interner),
- binders: chalk_ir::Binders::new(binders, bound),
- })
- }
-
- fn impl_datum(
- &self,
- impl_id: chalk_ir::ImplId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::ImplDatum<RustInterner<'tcx>>> {
- let def_id = impl_id.0;
- let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
- let binders = binders_for(self.interner, bound_vars);
-
- 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 where_clauses = self.where_clauses_for(def_id, bound_vars);
-
- let value = chalk_solve::rust_ir::ImplDatumBound {
- trait_ref: trait_ref.lower_into(self.interner),
- 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: 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,
- })
- }
-
- fn impls_for_trait(
- &self,
- trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
- parameters: &[chalk_ir::GenericArg<RustInterner<'tcx>>],
- _binders: &chalk_ir::CanonicalVarKinds<RustInterner<'tcx>>,
- ) -> Vec<chalk_ir::ImplId<RustInterner<'tcx>>> {
- let def_id = trait_id.0;
-
- // FIXME(chalk): use TraitDef::for_each_relevant_impl, but that will
- // require us to be able to interconvert `Ty<'tcx>`, and we're
- // not there yet.
-
- let all_impls = self.interner.tcx.all_impls(def_id);
- let matched_impls = all_impls.filter(|impl_def_id| {
- use chalk_ir::could_match::CouldMatch;
- let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap();
- let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id);
-
- let self_ty = trait_ref.map_bound(|t| t.self_ty());
- let self_ty = self_ty.subst(self.interner.tcx, bound_vars);
- let lowered_ty = self_ty.lower_into(self.interner);
-
- parameters[0].assert_ty_ref(self.interner).could_match(
- self.interner,
- self.unification_database(),
- &lowered_ty,
- )
- });
-
- let impls = matched_impls.map(chalk_ir::ImplId).collect();
- impls
- }
-
- fn impl_provided_for(
- &self,
- auto_trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
- chalk_ty: &chalk_ir::TyKind<RustInterner<'tcx>>,
- ) -> bool {
- use chalk_ir::Scalar::*;
- 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().subst_identity();
- let self_ty = trait_ref.self_ty();
- 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))) => matches!(
- (ty1, ty2),
- (ty::IntTy::Isize, chalk_ir::IntTy::Isize)
- | (ty::IntTy::I8, chalk_ir::IntTy::I8)
- | (ty::IntTy::I16, chalk_ir::IntTy::I16)
- | (ty::IntTy::I32, chalk_ir::IntTy::I32)
- | (ty::IntTy::I64, chalk_ir::IntTy::I64)
- | (ty::IntTy::I128, chalk_ir::IntTy::I128)
- ),
- (ty::Uint(ty1), Scalar(Uint(ty2))) => matches!(
- (ty1, ty2),
- (ty::UintTy::Usize, chalk_ir::UintTy::Usize)
- | (ty::UintTy::U8, chalk_ir::UintTy::U8)
- | (ty::UintTy::U16, chalk_ir::UintTy::U16)
- | (ty::UintTy::U32, chalk_ir::UintTy::U32)
- | (ty::UintTy::U64, chalk_ir::UintTy::U64)
- | (ty::UintTy::U128, chalk_ir::UintTy::U128)
- ),
- (ty::Float(ty1), Scalar(Float(ty2))) => matches!(
- (ty1, ty2),
- (ty::FloatTy::F32, chalk_ir::FloatTy::F32)
- | (ty::FloatTy::F64, chalk_ir::FloatTy::F64)
- ),
- (&ty::Tuple(substs), Tuple(len, _)) => substs.len() == *len,
- (&ty::Array(..), Array(..)) => true,
- (&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,
- (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false,
- (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::Alias(ty::Opaque, ty::AliasTy { 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::Foreign(def_id), Foreign(foreign_def_id)) => def_id == foreign_def_id.0,
- (&ty::Error(..), Error) => false,
- _ => false,
- };
- if provides {
- return true;
- }
- }
- false
- }
-
- fn associated_ty_value(
- &self,
- associated_ty_id: chalk_solve::rust_ir::AssociatedTyValueId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::AssociatedTyValue<RustInterner<'tcx>>> {
- let def_id = associated_ty_id.0;
- let assoc_item = self.interner.tcx.associated_item(def_id);
- let impl_id = assoc_item.container_id(self.interner.tcx);
- match assoc_item.kind {
- AssocKind::Type => {}
- _ => unimplemented!("Not possible??"),
- }
-
- let trait_item_id = assoc_item.trait_item_def_id.expect("assoc_ty with no trait version");
- let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
- let binders = binders_for(self.interner, bound_vars);
- let ty = self
- .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(trait_item_id),
- value: chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::AssociatedTyValueBound { ty },
- ),
- })
- }
-
- fn custom_clauses(&self) -> Vec<chalk_ir::ProgramClause<RustInterner<'tcx>>> {
- vec![]
- }
-
- fn local_impls_to_coherence_check(
- &self,
- _trait_id: chalk_ir::TraitId<RustInterner<'tcx>>,
- ) -> Vec<chalk_ir::ImplId<RustInterner<'tcx>>> {
- unimplemented!()
- }
-
- fn opaque_ty_data(
- &self,
- opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::OpaqueTyDatum<RustInterner<'tcx>>> {
- 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 explicit_item_bounds = self.interner.tcx.explicit_item_bounds(opaque_ty_id.0);
- let bounds =
- explicit_item_bounds
- .subst_iter_copied(self.interner.tcx, &bound_vars)
- .map(|(bound, _)| {
- bound.fold_with(&mut ReplaceOpaqueTyFolder {
- tcx: self.interner.tcx,
- opaque_ty_id,
- identity_substs,
- binder_index: ty::INNERMOST,
- })
- })
- .filter_map(|bound| {
- LowerInto::<
- Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
- >::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(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::new(binders, value),
- })
- }
-
- fn program_clauses_for_env(
- &self,
- environment: &chalk_ir::Environment<RustInterner<'tcx>>,
- ) -> chalk_ir::ProgramClauses<RustInterner<'tcx>> {
- chalk_solve::program_clauses_for_env(self, environment)
- }
-
- fn well_known_trait_id(
- &self,
- well_known_trait: chalk_solve::rust_ir::WellKnownTrait,
- ) -> Option<chalk_ir::TraitId<RustInterner<'tcx>>> {
- use chalk_solve::rust_ir::WellKnownTrait::*;
- let lang_items = self.interner.tcx.lang_items();
- let def_id = match well_known_trait {
- Sized => lang_items.sized_trait(),
- Copy => lang_items.copy_trait(),
- Clone => lang_items.clone_trait(),
- Drop => lang_items.drop_trait(),
- Fn => lang_items.fn_trait(),
- FnMut => lang_items.fn_mut_trait(),
- FnOnce => lang_items.fn_once_trait(),
- Generator => lang_items.gen_trait(),
- Unsize => lang_items.unsize_trait(),
- Unpin => lang_items.unpin_trait(),
- CoerceUnsized => lang_items.coerce_unsized_trait(),
- DiscriminantKind => lang_items.discriminant_kind_trait(),
- DispatchFromDyn => lang_items.dispatch_from_dyn_trait(),
- Tuple => lang_items.tuple_trait(),
- };
- def_id.map(chalk_ir::TraitId)
- }
-
- fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
- self.interner.tcx.check_is_object_safe(trait_id.0)
- }
-
- fn hidden_opaque_type(
- &self,
- _id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
- ) -> chalk_ir::Ty<RustInterner<'tcx>> {
- // FIXME(chalk): actually get hidden ty
- self.interner.tcx.types.unit.lower_into(self.interner)
- }
-
- fn closure_kind(
- &self,
- _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
- substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
- ) -> chalk_solve::rust_ir::ClosureKind {
- let kind = &substs.as_slice(self.interner)[substs.len(self.interner) - 3];
- 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"),
- }
- }
-
- fn closure_inputs_and_output(
- &self,
- _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
- substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
- ) -> chalk_ir::Binders<chalk_solve::rust_ir::FnDefInputsAndOutputDatum<RustInterner<'tcx>>>
- {
- let sig = &substs.as_slice(self.interner)[substs.len(self.interner) - 2];
- match sig.assert_ty_ref(self.interner).kind(self.interner) {
- chalk_ir::TyKind::Function(f) => {
- let substitution = f.substitution.0.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.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."),
- };
-
- chalk_ir::Binders::new(
- chalk_ir::VariableKinds::from_iter(
- self.interner,
- (0..f.num_binders).map(|_| chalk_ir::VariableKind::Lifetime),
- ),
- chalk_solve::rust_ir::FnDefInputsAndOutputDatum { argument_types, return_type },
- )
- }
- _ => panic!("Invalid sig."),
- }
- }
-
- fn closure_upvars(
- &self,
- _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
- substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
- ) -> chalk_ir::Binders<chalk_ir::Ty<RustInterner<'tcx>>> {
- let inputs_and_output = self.closure_inputs_and_output(_closure_id, substs);
- let tuple = substs.as_slice(self.interner).last().unwrap().assert_ty_ref(self.interner);
- inputs_and_output.map_ref(|_| tuple.clone())
- }
-
- fn closure_fn_substitution(
- &self,
- _closure_id: chalk_ir::ClosureId<RustInterner<'tcx>>,
- substs: &chalk_ir::Substitution<RustInterner<'tcx>>,
- ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
- 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<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::GeneratorDatum<RustInterner<'tcx>>> {
- unimplemented!()
- }
-
- fn generator_witness_datum(
- &self,
- _generator_id: chalk_ir::GeneratorId<RustInterner<'tcx>>,
- ) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<RustInterner<'tcx>>> {
- unimplemented!()
- }
-
- fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<RustInterner<'tcx>> {
- self
- }
-
- fn discriminant_type(
- &self,
- _: chalk_ir::Ty<RustInterner<'tcx>>,
- ) -> chalk_ir::Ty<RustInterner<'tcx>> {
- unimplemented!()
- }
-}
-
-impl<'tcx> chalk_ir::UnificationDatabase<RustInterner<'tcx>> for RustIrDatabase<'tcx> {
- fn fn_def_variance(
- &self,
- def_id: chalk_ir::FnDefId<RustInterner<'tcx>>,
- ) -> chalk_ir::Variances<RustInterner<'tcx>> {
- let variances = self.interner.tcx.variances_of(def_id.0);
- chalk_ir::Variances::from_iter(
- self.interner,
- variances.iter().map(|v| v.lower_into(self.interner)),
- )
- }
-
- fn adt_variance(
- &self,
- adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
- ) -> chalk_ir::Variances<RustInterner<'tcx>> {
- let variances = self.interner.tcx.variances_of(adt_id.0.did());
- chalk_ir::Variances::from_iter(
- self.interner,
- variances.iter().map(|v| v.lower_into(self.interner)),
- )
- }
-}
-
-/// Creates an `InternalSubsts` that maps each generic parameter to a higher-ranked
-/// var bound at index `0`. For types, we use a `BoundVar` index equal to
-/// the type parameter index. For regions, we use the `BoundRegionKind::BrNamed`
-/// variant (which has a `DefId`).
-fn bound_vars_for_item(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> {
- InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind {
- ty::GenericParamDefKind::Type { .. } => tcx
- .mk_bound(
- ty::INNERMOST,
- ty::BoundTy {
- var: ty::BoundVar::from(param.index),
- kind: ty::BoundTyKind::Param(param.def_id, param.name),
- },
- )
- .into(),
-
- ty::GenericParamDefKind::Lifetime => {
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_usize(substs.len()),
- kind: ty::BrAnon(None),
- };
- tcx.mk_re_late_bound(ty::INNERMOST, br).into()
- }
-
- ty::GenericParamDefKind::Const { .. } => tcx
- .mk_const(
- ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)),
- tcx.type_of(param.def_id).subst_identity(),
- )
- .into(),
- })
-}
-
-fn binders_for<'tcx>(
- interner: RustInterner<'tcx>,
- bound_vars: SubstsRef<'tcx>,
-) -> chalk_ir::VariableKinds<RustInterner<'tcx>> {
- chalk_ir::VariableKinds::from_iter(
- interner,
- 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::TyVariableKind::General)
- }
- ty::subst::GenericArgKind::Const(c) => {
- chalk_ir::VariableKind::Const(c.ty().lower_into(interner))
- }
- }),
- )
-}
-
-struct ReplaceOpaqueTyFolder<'tcx> {
- tcx: TyCtxt<'tcx>,
- opaque_ty_id: chalk_ir::OpaqueTyId<RustInterner<'tcx>>,
- identity_substs: SubstsRef<'tcx>,
- binder_index: ty::DebruijnIndex,
-}
-
-impl<'tcx> ty::TypeFolder<TyCtxt<'tcx>> for ReplaceOpaqueTyFolder<'tcx> {
- fn interner(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
- &mut self,
- t: ty::Binder<'tcx, T>,
- ) -> ty::Binder<'tcx, T> {
- self.binder_index.shift_in(1);
- let t = t.super_fold_with(self);
- self.binder_index.shift_out(1);
- t
- }
-
- fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *ty.kind() {
- if def_id == self.opaque_ty_id.0 && substs == self.identity_substs {
- return self
- .tcx
- .mk_bound(self.binder_index, ty::BoundTy::from(ty::BoundVar::from_u32(0)));
- }
- }
- ty
- }
-}
diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs
deleted file mode 100644
index e447ab94f..000000000
--- a/compiler/rustc_traits/src/chalk/lowering.rs
+++ /dev/null
@@ -1,1241 +0,0 @@
-//! Contains the logic to lower rustc types into Chalk types
-//!
-//! In many cases there is a 1:1 relationship between a rustc type and a Chalk type.
-//! For example, a `SubstsRef` maps almost directly to a `Substitution`. In some
-//! other cases, such as `Param`s, there is no Chalk type, so we have to handle
-//! accordingly.
-//!
-//! ## `Ty` lowering
-//! Much of the `Ty` lowering is 1:1 with Chalk. (Or will be eventually). A
-//! helpful table for what types lower to what can be found in the
-//! [Chalk book](https://rust-lang.github.io/chalk/book/types/rust_types.html).
-//! The most notable difference lies with `Param`s. To convert from rustc to
-//! Chalk, we eagerly and deeply convert `Param`s to placeholders (in goals) or
-//! bound variables (for clause generation through functions in `db`).
-//!
-//! ## `Region` lowering
-//! Regions are handled in rustc and Chalk is quite differently. In rustc, there
-//! is a difference between "early bound" and "late bound" regions, where only
-//! the late bound regions have a `DebruijnIndex`. Moreover, in Chalk all
-//! regions (Lifetimes) have an associated index. In rustc, only `BrAnon`s have
-//! an index, whereas `BrNamed` don't. In order to lower regions to Chalk, we
-//! convert all regions into `BrAnon` late-bound regions.
-//!
-//! ## `Const` lowering
-//! Chalk doesn't handle consts currently, so consts are currently lowered to
-//! an empty tuple.
-//!
-//! ## Bound variable collection
-//! Another difference between rustc and Chalk lies in the handling of binders.
-//! Chalk requires that we store the bound parameter kinds, whereas rustc does
-//! 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::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{
- self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
- TypeSuperVisitable, TypeVisitable, 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
-pub(crate) trait LowerInto<'tcx, T> {
- /// Lower a rustc construct (e.g., `ty::TraitPredicate`) to a chalk type, consuming `self`.
- fn lower_into(self, interner: RustInterner<'tcx>) -> T;
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>> for SubstsRef<'tcx> {
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
- chalk_ir::Substitution::from_iter(interner, self.iter().map(|s| s.lower_into(interner)))
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Substitution<RustInterner<'tcx>>>
- for &'tcx ty::List<Ty<'tcx>>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_ir::Substitution<RustInterner<'tcx>> {
- chalk_ir::Substitution::from_iter(
- interner,
- self.iter().map(|ty| GenericArg::from(ty).lower_into(interner)),
- )
- }
-}
-
-impl<'tcx> LowerInto<'tcx, SubstsRef<'tcx>> for &chalk_ir::Substitution<RustInterner<'tcx>> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> SubstsRef<'tcx> {
- interner
- .tcx
- .mk_substs_from_iter(self.iter(interner).map(|subst| subst.lower_into(interner)))
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>>>
- for ChalkEnvironmentAndGoal<'tcx>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_ir::InEnvironment<chalk_ir::Goal<RustInterner<'tcx>>> {
- let clauses = self.environment.into_iter().map(|predicate| {
- let (predicate, binders, _named_regions) =
- collect_bound_vars(interner, interner.tcx, predicate.kind());
- let consequence = match predicate {
- ty::PredicateKind::TypeWellFormedFromEnv(ty) => {
- chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner)))
- }
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
- chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Trait(
- predicate.trait_ref.lower_into(interner),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
- chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::LifetimeOutlives(
- chalk_ir::LifetimeOutlives {
- a: predicate.0.lower_into(interner),
- b: predicate.1.lower_into(interner),
- },
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
- chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::TypeOutlives(
- chalk_ir::TypeOutlives {
- ty: predicate.0.lower_into(interner),
- lifetime: predicate.1.lower_into(interner),
- },
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
- chalk_ir::DomainGoal::Holds(chalk_ir::WhereClause::AliasEq(
- predicate.lower_into(interner),
- ))
- }
- ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
- ty::GenericArgKind::Type(ty) => chalk_ir::DomainGoal::WellFormed(
- chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
- ),
- // FIXME(chalk): we need to change `WellFormed` in Chalk to take a `GenericArg`
- _ => chalk_ir::DomainGoal::WellFormed(chalk_ir::WellFormed::Ty(
- interner.tcx.types.unit.lower_into(interner),
- )),
- },
- ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::ConstEquate(..) => bug!("unexpected predicate {}", predicate),
- };
- let value = chalk_ir::ProgramClauseImplication {
- consequence,
- conditions: chalk_ir::Goals::empty(interner),
- priority: chalk_ir::ClausePriority::High,
- constraints: chalk_ir::Constraints::empty(interner),
- };
- chalk_ir::ProgramClauseData(chalk_ir::Binders::new(binders, value)).intern(interner)
- });
-
- let goal: chalk_ir::GoalData<RustInterner<'tcx>> = self.goal.lower_into(interner);
- chalk_ir::InEnvironment {
- environment: chalk_ir::Environment {
- clauses: chalk_ir::ProgramClauses::from_iter(interner, clauses),
- },
- goal: goal.intern(interner),
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predicate<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::GoalData<RustInterner<'tcx>> {
- let (predicate, binders, _named_regions) =
- collect_bound_vars(interner, interner.tcx, self.kind());
-
- let value = match predicate {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
- chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
- chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
- chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
- chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
- a: predicate.0.lower_into(interner),
- b: predicate.1.lower_into(interner),
- }),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
- chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
- chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
- ty: predicate.0.lower_into(interner),
- lifetime: predicate.1.lower_into(interner),
- }),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
- chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(
- chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)),
- ))
- }
- ty::PredicateKind::WellFormed(arg) => match arg.unpack() {
- GenericArgKind::Type(ty) => match ty.kind() {
- // FIXME(chalk): In Chalk, a placeholder is WellFormed if it
- // `FromEnv`. However, when we "lower" Params, we don't update
- // the environment.
- ty::Placeholder(..) => {
- chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
- }
-
- _ => chalk_ir::GoalData::DomainGoal(chalk_ir::DomainGoal::WellFormed(
- chalk_ir::WellFormed::Ty(ty.lower_into(interner)),
- )),
- },
- // FIXME(chalk): handle well formed consts
- GenericArgKind::Const(..) => {
- chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
- }
- GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt),
- },
-
- ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal(
- chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)),
- ),
-
- ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => {
- chalk_ir::GoalData::SubtypeGoal(chalk_ir::SubtypeGoal {
- a: a.lower_into(interner),
- b: b.lower_into(interner),
- })
- }
-
- // FIXME(chalk): other predicates
- //
- // We can defer this, but ultimately we'll want to express
- // some of these in terms of chalk operations.
- ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::ConstEquate(..) => {
- chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner))
- }
- ty::PredicateKind::TypeWellFormedFromEnv(ty) => chalk_ir::GoalData::DomainGoal(
- chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))),
- ),
- };
-
- chalk_ir::GoalData::Quantified(
- chalk_ir::QuantifierKind::ForAll,
- chalk_ir::Binders::new(binders, value.intern(interner)),
- )
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::TraitRef<RustInterner<'tcx>>>
- for rustc_middle::ty::TraitRef<'tcx>
-{
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::TraitRef<RustInterner<'tcx>> {
- chalk_ir::TraitRef {
- trait_id: chalk_ir::TraitId(self.def_id),
- substitution: self.substs.lower_into(interner),
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq<RustInterner<'tcx>>>
- for rustc_middle::ty::ProjectionPredicate<'tcx>
-{
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::AliasEq<RustInterner<'tcx>> {
- // FIXME(associated_const_equality): teach chalk about terms for alias eq.
- chalk_ir::AliasEq {
- ty: self.term.ty().unwrap().lower_into(interner),
- alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
- associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.def_id),
- substitution: self.projection_ty.substs.lower_into(interner),
- }),
- }
- }
-}
-
-/*
-// FIXME(...): Where do I add this to Chalk? I can't find it in the rustc repo anywhere.
-impl<'tcx> LowerInto<'tcx, chalk_ir::Term<RustInterner<'tcx>>> for rustc_middle::ty::Term<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Term<RustInterner<'tcx>> {
- match self {
- ty::Term::Ty(ty) => ty.lower_into(interner).into(),
- ty::Term::Const(c) => c.lower_into(interner).into(),
- }
- }
-}
-*/
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Ty<RustInterner<'tcx>> {
- 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() {
- 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 {
- ty::IntTy::Isize => int(chalk_ir::IntTy::Isize),
- ty::IntTy::I8 => int(chalk_ir::IntTy::I8),
- ty::IntTy::I16 => int(chalk_ir::IntTy::I16),
- ty::IntTy::I32 => int(chalk_ir::IntTy::I32),
- ty::IntTy::I64 => int(chalk_ir::IntTy::I64),
- ty::IntTy::I128 => int(chalk_ir::IntTy::I128),
- },
- ty::Uint(ty) => match ty {
- ty::UintTy::Usize => uint(chalk_ir::UintTy::Usize),
- ty::UintTy::U8 => uint(chalk_ir::UintTy::U8),
- ty::UintTy::U16 => uint(chalk_ir::UintTy::U16),
- ty::UintTy::U32 => uint(chalk_ir::UintTy::U32),
- ty::UintTy::U64 => uint(chalk_ir::UintTy::U64),
- ty::UintTy::U128 => uint(chalk_ir::UintTy::U128),
- },
- ty::Float(ty) => match ty {
- ty::FloatTy::F32 => float(chalk_ir::FloatTy::F32),
- ty::FloatTy::F64 => float(chalk_ir::FloatTy::F64),
- },
- ty::Adt(def, substs) => {
- chalk_ir::TyKind::Adt(chalk_ir::AdtId(def), substs.lower_into(interner))
- }
- 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))
- }
- 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))
- }
- ty::Ref(region, ty, mutability) => chalk_ir::TyKind::Ref(
- mutability.lower_into(interner),
- region.lower_into(interner),
- ty.lower_into(interner),
- ),
- 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());
- chalk_ir::TyKind::Function(chalk_ir::FnPointer {
- num_binders: binders.len(interner),
- sig: sig.lower_into(interner),
- substitution: chalk_ir::FnSubst(chalk_ir::Substitution::from_iter(
- interner,
- inputs_and_outputs.iter().map(|ty| {
- chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner)
- }),
- )),
- })
- }
- // FIXME(dyn-star): handle the dynamic kind (dyn or dyn*)
- ty::Dynamic(predicates, region, _kind) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy {
- bounds: predicates.lower_into(interner),
- lifetime: region.lower_into(interner),
- }),
- ty::Closure(def_id, substs) => {
- chalk_ir::TyKind::Closure(chalk_ir::ClosureId(def_id), substs.lower_into(interner))
- }
- ty::Generator(def_id, substs, _) => chalk_ir::TyKind::Generator(
- chalk_ir::GeneratorId(def_id),
- substs.lower_into(interner),
- ),
- ty::GeneratorWitness(_) => unimplemented!(),
- ty::GeneratorWitnessMIR(..) => unimplemented!(),
- ty::Never => chalk_ir::TyKind::Never,
- ty::Tuple(types) => chalk_ir::TyKind::Tuple(types.len(), types.lower_into(interner)),
- ty::Alias(ty::Projection, ty::AliasTy { def_id, substs, .. }) => {
- chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
- associated_ty_id: chalk_ir::AssocTypeId(def_id),
- substitution: substs.lower_into(interner),
- }))
- }
- ty::Alias(ty::Inherent, _) => unimplemented!(),
- ty::Alias(ty::Opaque, ty::AliasTy { 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),
- }))
- }
- // This should have been done eagerly prior to this, and all Params
- // should have been substituted to placeholders
- 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(),
- )),
- ty::Placeholder(_placeholder) => {
- chalk_ir::TyKind::Placeholder(chalk_ir::PlaceholderIndex {
- ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() },
- idx: _placeholder.bound.var.as_usize(),
- })
- }
- ty::Infer(_infer) => unimplemented!(),
- ty::Error(_) => chalk_ir::TyKind::Error,
- }
- .intern(interner)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty<RustInterner<'tcx>> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> Ty<'tcx> {
- use chalk_ir::TyKind;
-
- 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(ty::IntTy::Isize),
- chalk_ir::IntTy::I8 => ty::Int(ty::IntTy::I8),
- chalk_ir::IntTy::I16 => ty::Int(ty::IntTy::I16),
- chalk_ir::IntTy::I32 => ty::Int(ty::IntTy::I32),
- chalk_ir::IntTy::I64 => ty::Int(ty::IntTy::I64),
- chalk_ir::IntTy::I128 => ty::Int(ty::IntTy::I128),
- },
- chalk_ir::Scalar::Uint(int_ty) => match int_ty {
- chalk_ir::UintTy::Usize => ty::Uint(ty::UintTy::Usize),
- chalk_ir::UintTy::U8 => ty::Uint(ty::UintTy::U8),
- chalk_ir::UintTy::U16 => ty::Uint(ty::UintTy::U16),
- chalk_ir::UintTy::U32 => ty::Uint(ty::UintTy::U32),
- chalk_ir::UintTy::U64 => ty::Uint(ty::UintTy::U64),
- chalk_ir::UintTy::U128 => ty::Uint(ty::UintTy::U128),
- },
- chalk_ir::Scalar::Float(float_ty) => match float_ty {
- chalk_ir::FloatTy::F32 => ty::Float(ty::FloatTy::F32),
- chalk_ir::FloatTy::F64 => ty::Float(ty::FloatTy::F64),
- },
- },
- TyKind::Array(ty, c) => {
- let ty = ty.lower_into(interner);
- let c = c.lower_into(interner);
- ty::Array(ty, 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(generator, substitution) => ty::Generator(
- generator.0,
- substitution.lower_into(interner),
- ast::Movability::Static,
- ),
- TyKind::GeneratorWitness(..) => unimplemented!(),
- TyKind::Never => ty::Never,
- TyKind::Tuple(_len, substitution) => {
- ty::Tuple(substitution.lower_into(interner).into_type_list(interner.tcx))
- }
- 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::Alias(
- ty::Opaque,
- interner.tcx.mk_alias_ty(opaque_ty.0, substitution.lower_into(interner)),
- ),
- TyKind::AssociatedType(assoc_ty, substitution) => ty::Alias(
- ty::Projection,
- interner.tcx.mk_alias_ty(assoc_ty.0, substitution.lower_into(interner)),
- ),
- TyKind::Foreign(def_id) => ty::Foreign(def_id.0),
- TyKind::Error => return interner.tcx.ty_error_misc(),
- TyKind::Alias(alias_ty) => match alias_ty {
- chalk_ir::AliasTy::Projection(projection) => ty::Alias(
- ty::Projection,
- interner.tcx.mk_alias_ty(
- projection.associated_ty_id.0,
- projection.substitution.lower_into(interner),
- ),
- ),
- chalk_ir::AliasTy::Opaque(opaque) => ty::Alias(
- ty::Opaque,
- interner.tcx.mk_alias_ty(
- 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,
- },
- ),
- TyKind::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder {
- universe: ty::UniverseIndex::from_usize(placeholder.ui.counter),
- bound: ty::BoundTy {
- var: ty::BoundVar::from_usize(placeholder.idx),
- kind: ty::BoundTyKind::Anon,
- },
- }),
- TyKind::InferenceVar(_, _) => unimplemented!(),
- TyKind::Dyn(_) => unimplemented!(),
- };
- interner.tcx.mk_ty_from_kind(kind)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Lifetime<RustInterner<'tcx>> {
- match *self {
- ty::ReEarlyBound(_) => {
- panic!("Should have already been substituted.");
- }
- ty::ReError(_) => {
- panic!("Error lifetime should not have already been lowered.");
- }
- ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
- chalk_ir::DebruijnIndex::new(db.as_u32()),
- br.var.as_usize(),
- ))
- .intern(interner),
- ty::ReFree(_) => unimplemented!(),
- ty::ReStatic => chalk_ir::LifetimeData::Static.intern(interner),
- ty::ReVar(_) => unimplemented!(),
- ty::RePlaceholder(placeholder_region) => {
- chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex {
- ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() },
- idx: 0, // FIXME: This `idx: 0` is sus.
- })
- .intern(interner)
- }
- ty::ReErased => chalk_ir::LifetimeData::Erased.intern(interner),
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'tcx>> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> Region<'tcx> {
- let tcx = interner.tcx;
- match self.data(interner) {
- chalk_ir::LifetimeData::BoundVar(var) => tcx.mk_re_late_bound(
- ty::DebruijnIndex::from_u32(var.debruijn.depth()),
- ty::BoundRegion {
- var: ty::BoundVar::from_usize(var.index),
- kind: ty::BrAnon(None),
- },
- ),
- chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
- chalk_ir::LifetimeData::Placeholder(p) => tcx.mk_re_placeholder(ty::Placeholder {
- universe: ty::UniverseIndex::from_usize(p.ui.counter),
- bound: ty::BoundRegion {
- var: ty::BoundVar::from_usize(p.idx),
- kind: ty::BoundRegionKind::BrAnon(None),
- },
- }),
- chalk_ir::LifetimeData::Static => tcx.lifetimes.re_static,
- chalk_ir::LifetimeData::Erased => tcx.lifetimes.re_erased,
- chalk_ir::LifetimeData::Phantom(void, _) => match *void {},
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Const<RustInterner<'tcx>>> for ty::Const<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Const<RustInterner<'tcx>> {
- let ty = self.ty().lower_into(interner);
- let value = match self.kind() {
- 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. {:?}", self),
- };
- chalk_ir::ConstData { ty, value }.intern(interner)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, ty::Const<'tcx>> for &chalk_ir::Const<RustInterner<'tcx>> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> ty::Const<'tcx> {
- let data = self.data(interner);
- let ty = data.ty.lower_into(interner);
- let kind = match data.value {
- chalk_ir::ConstValue::BoundVar(var) => ty::ConstKind::Bound(
- ty::DebruijnIndex::from_u32(var.debruijn.depth()),
- ty::BoundVar::from_u32(var.index as u32),
- ),
- chalk_ir::ConstValue::InferenceVar(_var) => unimplemented!(),
- chalk_ir::ConstValue::Placeholder(_p) => unimplemented!(),
- chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned),
- };
- interner.tcx.mk_const(kind, ty)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::GenericArg<RustInterner<'tcx>>> for GenericArg<'tcx> {
- fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::GenericArg<RustInterner<'tcx>> {
- match self.unpack() {
- ty::subst::GenericArgKind::Type(ty) => {
- chalk_ir::GenericArgData::Ty(ty.lower_into(interner))
- }
- ty::subst::GenericArgKind::Lifetime(lifetime) => {
- chalk_ir::GenericArgData::Lifetime(lifetime.lower_into(interner))
- }
- ty::subst::GenericArgKind::Const(c) => {
- chalk_ir::GenericArgData::Const(c.lower_into(interner))
- }
- }
- .intern(interner)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, ty::subst::GenericArg<'tcx>>
- for &chalk_ir::GenericArg<RustInterner<'tcx>>
-{
- fn lower_into(self, interner: RustInterner<'tcx>) -> ty::subst::GenericArg<'tcx> {
- match self.data(interner) {
- chalk_ir::GenericArgData::Ty(ty) => {
- let t: Ty<'tcx> = ty.lower_into(interner);
- t.into()
- }
- chalk_ir::GenericArgData::Lifetime(lifetime) => {
- let r: Region<'tcx> = lifetime.lower_into(interner);
- r.into()
- }
- chalk_ir::GenericArgData::Const(c) => {
- let c: ty::Const<'tcx> = c.lower_into(interner);
- c.into()
- }
- }
- }
-}
-
-// We lower into an Option here since there are some predicates which Chalk
-// doesn't have a representation for yet (as a `WhereClause`), but are so common
-// that we just are accepting the unsoundness for now. The `Option` will
-// eventually be removed.
-impl<'tcx> LowerInto<'tcx, Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>>
- for ty::Predicate<'tcx>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
- let (predicate, binders, _named_regions) =
- collect_bound_vars(interner, interner.tcx, self.kind());
- let value = match predicate {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
- Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner)))
- }
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
- Some(chalk_ir::WhereClause::LifetimeOutlives(chalk_ir::LifetimeOutlives {
- a: predicate.0.lower_into(interner),
- b: predicate.1.lower_into(interner),
- }))
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
- Some(chalk_ir::WhereClause::TypeOutlives(chalk_ir::TypeOutlives {
- ty: predicate.0.lower_into(interner),
- lifetime: predicate.1.lower_into(interner),
- }))
- }
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
- Some(chalk_ir::WhereClause::AliasEq(predicate.lower_into(interner)))
- }
- ty::PredicateKind::WellFormed(_ty) => None,
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None,
-
- ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("unexpected predicate {self}")
- }
- };
- value.map(|value| chalk_ir::Binders::new(binders, value))
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>>>
- for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_ir::Binders<chalk_ir::QuantifiedWhereClauses<RustInterner<'tcx>>> {
- // `Self` has one binder:
- // Binder<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>
- // The return type has two:
- // Binders<&[Binders<WhereClause<I>>]>
- // This means that any variables that are escaping `self` need to be
- // shifted in by one so that they are still escaping.
- let predicates = ty::fold::shift_vars(interner.tcx, self, 1);
-
- let self_ty = interner.tcx.mk_bound(
- // This is going to be wrapped in a binder
- ty::DebruijnIndex::from_usize(1),
- ty::BoundTy { var: ty::BoundVar::from_usize(0), kind: ty::BoundTyKind::Anon },
- );
- let where_clauses = predicates.into_iter().map(|predicate| {
- let (predicate, binders, _named_regions) =
- collect_bound_vars(interner, interner.tcx, predicate);
- match predicate {
- ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef { def_id, substs }) => {
- chalk_ir::Binders::new(
- binders.clone(),
- chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef {
- trait_id: chalk_ir::TraitId(def_id),
- substitution: interner
- .tcx
- .mk_substs_trait(self_ty, substs)
- .lower_into(interner),
- }),
- )
- }
- 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.def_id),
- substitution: interner
- .tcx
- .mk_substs_trait(self_ty, predicate.substs)
- .lower_into(interner),
- }),
- // FIXME(associated_const_equality): teach chalk about terms for alias eq.
- ty: predicate.term.ty().unwrap().lower_into(interner),
- }),
- ),
- ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new(
- 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(existential_binder, value)
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig<RustInterner<'tcx>>>
- for ty::Binder<'tcx, ty::FnSig<'tcx>>
-{
- fn lower_into(self, _interner: RustInterner<'_>) -> FnSig<RustInterner<'tcx>> {
- chalk_ir::FnSig {
- abi: self.abi(),
- safety: match self.unsafety() {
- Unsafety::Normal => chalk_ir::Safety::Safe,
- Unsafety::Unsafe => chalk_ir::Safety::Unsafe,
- },
- variadic: self.c_variadic(),
- }
- }
-}
-
-// We lower into an Option here since there are some predicates which Chalk
-// doesn't have a representation for yet (as an `InlineBound`). The `Option` will
-// eventually be removed.
-impl<'tcx> LowerInto<'tcx, Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>>>
- for ty::Predicate<'tcx>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> Option<chalk_solve::rust_ir::QuantifiedInlineBound<RustInterner<'tcx>>> {
- let (predicate, binders, _named_regions) =
- collect_bound_vars(interner, interner.tcx, self.kind());
- match predicate {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
- Some(chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::InlineBound::TraitBound(
- predicate.trait_ref.lower_into(interner),
- ),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
- Some(chalk_ir::Binders::new(
- binders,
- chalk_solve::rust_ir::InlineBound::AliasEqBound(predicate.lower_into(interner)),
- ))
- }
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_predicate)) => None,
- ty::PredicateKind::WellFormed(_ty) => None,
- ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => None,
-
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::ObjectSafe(..)
- | ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
- | ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => {
- bug!("unexpected predicate {}", &self)
- }
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>>>
- for ty::TraitRef<'tcx>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_solve::rust_ir::TraitBound<RustInterner<'tcx>> {
- 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_ir::Variance> for ty::Variance {
- fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Variance {
- match self {
- ty::Variance::Covariant => chalk_ir::Variance::Covariant,
- ty::Variance::Invariant => chalk_ir::Variance::Invariant,
- ty::Variance::Contravariant => chalk_ir::Variance::Contravariant,
- ty::Variance::Bivariant => unimplemented!(),
- }
- }
-}
-
-impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>>>
- for ty::ProjectionPredicate<'tcx>
-{
- fn lower_into(
- self,
- interner: RustInterner<'tcx>,
- ) -> chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>> {
- let (trait_ref, own_substs) = self.projection_ty.trait_ref_and_own_substs(interner.tcx);
- chalk_solve::rust_ir::AliasEqBound {
- trait_bound: trait_ref.lower_into(interner),
- associated_ty_id: chalk_ir::AssocTypeId(self.projection_ty.def_id),
- parameters: own_substs.iter().map(|arg| arg.lower_into(interner)).collect(),
- value: self.term.ty().unwrap().lower_into(interner),
- }
- }
-}
-
-/// To collect bound vars, we have to do two passes. In the first pass, we
-/// collect all `BoundRegionKind`s and `ty::Bound`s. In the second pass, we then
-/// replace `BrNamed` into `BrAnon`. The two separate passes are important,
-/// since we can only replace `BrNamed` with `BrAnon`s with indices *after* all
-/// "real" `BrAnon`s.
-///
-/// It's important to note that because of prior substitution, we may have
-/// late-bound regions, even outside of fn contexts, since this is the best way
-/// to prep types for chalk lowering.
-pub(crate) fn collect_bound_vars<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
- interner: RustInterner<'tcx>,
- tcx: TyCtxt<'tcx>,
- ty: Binder<'tcx, T>,
-) -> (T, chalk_ir::VariableKinds<RustInterner<'tcx>>, BTreeMap<DefId, u32>) {
- let mut bound_vars_collector = BoundVarsCollector::new();
- ty.as_ref().skip_binder().visit_with(&mut bound_vars_collector);
- let mut parameters = bound_vars_collector.parameters;
- let named_parameters: BTreeMap<DefId, u32> = bound_vars_collector
- .named_parameters
- .into_iter()
- .enumerate()
- .map(|(i, def_id)| (def_id, (i + parameters.len()) as u32))
- .collect();
-
- let mut bound_var_substitutor = NamedBoundVarSubstitutor::new(tcx, &named_parameters);
- let new_ty = ty.skip_binder().fold_with(&mut bound_var_substitutor);
-
- for var in named_parameters.values() {
- parameters.insert(*var, chalk_ir::VariableKind::Lifetime);
- }
-
- (0..parameters.len()).for_each(|i| {
- parameters
- .get(&(i as u32))
- .or_else(|| bug!("Skipped bound var index: parameters={:?}", parameters));
- });
-
- let binders = chalk_ir::VariableKinds::from_iter(interner, parameters.into_values());
-
- (new_ty, binders, named_parameters)
-}
-
-pub(crate) struct BoundVarsCollector<'tcx> {
- binder_index: ty::DebruijnIndex,
- pub(crate) parameters: BTreeMap<u32, chalk_ir::VariableKind<RustInterner<'tcx>>>,
- pub(crate) named_parameters: Vec<DefId>,
-}
-
-impl<'tcx> BoundVarsCollector<'tcx> {
- pub(crate) fn new() -> Self {
- BoundVarsCollector {
- binder_index: ty::INNERMOST,
- parameters: BTreeMap::new(),
- named_parameters: vec![],
- }
- }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for BoundVarsCollector<'tcx> {
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
- &mut self,
- t: &Binder<'tcx, T>,
- ) -> ControlFlow<Self::BreakTy> {
- 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>) -> ControlFlow<Self::BreakTy> {
- 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::TyVariableKind::General));
- }
- Entry::Occupied(entry) => match entry.get() {
- chalk_ir::VariableKind::Ty(_) => {}
- _ => panic!(),
- },
- }
- }
-
- _ => (),
- };
-
- t.super_visit_with(self)
- }
-
- fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match *r {
- ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
- ty::BoundRegionKind::BrNamed(def_id, _name) => {
- if !self.named_parameters.iter().any(|d| *d == def_id) {
- self.named_parameters.push(def_id);
- }
- }
-
- ty::BoundRegionKind::BrAnon(_) => match self.parameters.entry(br.var.as_u32()) {
- Entry::Vacant(entry) => {
- entry.insert(chalk_ir::VariableKind::Lifetime);
- }
- Entry::Occupied(entry) => match entry.get() {
- chalk_ir::VariableKind::Lifetime => {}
- _ => panic!(),
- },
- },
-
- ty::BoundRegionKind::BrEnv => unimplemented!(),
- },
-
- ty::ReEarlyBound(_re) => {
- // FIXME(chalk): jackh726 - I think we should always have already
- // substituted away `ReEarlyBound`s for `ReLateBound`s, but need to confirm.
- unimplemented!();
- }
-
- _ => (),
- };
-
- ControlFlow::Continue(())
- }
-}
-
-/// This is used to replace `BoundRegionKind::BrNamed` with `BoundRegionKind::BrAnon`.
-/// Note: we assume that we will always have room for more bound vars. (i.e. we
-/// won't ever hit the `u32` limit in `BrAnon`s).
-struct NamedBoundVarSubstitutor<'a, 'tcx> {
- tcx: TyCtxt<'tcx>,
- binder_index: ty::DebruijnIndex,
- named_parameters: &'a BTreeMap<DefId, u32>,
-}
-
-impl<'a, 'tcx> NamedBoundVarSubstitutor<'a, 'tcx> {
- fn new(tcx: TyCtxt<'tcx>, named_parameters: &'a BTreeMap<DefId, u32>) -> Self {
- NamedBoundVarSubstitutor { tcx, binder_index: ty::INNERMOST, named_parameters }
- }
-}
-
-impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for NamedBoundVarSubstitutor<'a, 'tcx> {
- fn interner(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
- &mut self,
- t: Binder<'tcx, T>,
- ) -> Binder<'tcx, T> {
- self.binder_index.shift_in(1);
- let result = t.super_fold_with(self);
- self.binder_index.shift_out(1);
- result
- }
-
- fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- match *r {
- ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
- ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) {
- Some(_) => {
- let new_br = ty::BoundRegion { var: br.var, kind: ty::BrAnon(None) };
- return self.tcx.mk_re_late_bound(index, new_br);
- }
- None => panic!("Missing `BrNamed`."),
- },
- ty::BrEnv => unimplemented!(),
- ty::BrAnon(..) => {}
- },
- _ => (),
- };
-
- r
- }
-}
-
-/// Used to substitute `Param`s with placeholders. We do this since Chalk
-/// have a notion of `Param`s.
-pub(crate) struct ParamsSubstitutor<'tcx> {
- tcx: TyCtxt<'tcx>,
- binder_index: ty::DebruijnIndex,
- list: Vec<rustc_middle::ty::ParamTy>,
- next_ty_placeholder: usize,
- pub(crate) params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
- pub(crate) named_regions: BTreeMap<DefId, u32>,
-}
-
-impl<'tcx> ParamsSubstitutor<'tcx> {
- pub(crate) fn new(tcx: TyCtxt<'tcx>, next_ty_placeholder: usize) -> Self {
- ParamsSubstitutor {
- tcx,
- binder_index: ty::INNERMOST,
- list: vec![],
- next_ty_placeholder,
- params: rustc_data_structures::fx::FxHashMap::default(),
- named_regions: BTreeMap::default(),
- }
- }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamsSubstitutor<'tcx> {
- fn interner(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
- &mut self,
- t: Binder<'tcx, T>,
- ) -> Binder<'tcx, T> {
- self.binder_index.shift_in(1);
- let result = t.super_fold_with(self);
- self.binder_index.shift_out(1);
- result
- }
-
- fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
- match *t.kind() {
- ty::Param(param) => match self.list.iter().position(|r| r == &param) {
- Some(idx) => self.tcx.mk_placeholder(ty::PlaceholderType {
- universe: ty::UniverseIndex::from_usize(0),
- bound: ty::BoundTy {
- var: ty::BoundVar::from_usize(idx),
- kind: ty::BoundTyKind::Anon,
- },
- }),
- None => {
- self.list.push(param);
- let idx = self.list.len() - 1 + self.next_ty_placeholder;
- self.params.insert(idx as u32, param);
- self.tcx.mk_placeholder(ty::PlaceholderType {
- universe: ty::UniverseIndex::from_usize(0),
- bound: ty::BoundTy {
- var: ty::BoundVar::from_usize(idx),
- kind: ty::BoundTyKind::Anon,
- },
- })
- }
- },
- _ => t.super_fold_with(self),
- }
- }
-
- fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- match *r {
- // FIXME(chalk) - jackh726 - this currently isn't hit in any tests,
- // since canonicalization will already change these to canonical
- // variables (ty::ReLateBound).
- ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) {
- Some(idx) => {
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_u32(*idx),
- kind: ty::BrAnon(None),
- };
- self.tcx.mk_re_late_bound(self.binder_index, br)
- }
- None => {
- let idx = self.named_regions.len() as u32;
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_u32(idx),
- kind: ty::BrAnon(None),
- };
- self.named_regions.insert(_re.def_id, idx);
- self.tcx.mk_re_late_bound(self.binder_index, br)
- }
- },
-
- _ => r,
- }
- }
-}
-
-pub(crate) struct ReverseParamsSubstitutor<'tcx> {
- tcx: TyCtxt<'tcx>,
- params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
-}
-
-impl<'tcx> ReverseParamsSubstitutor<'tcx> {
- pub(crate) fn new(
- tcx: TyCtxt<'tcx>,
- params: rustc_data_structures::fx::FxHashMap<u32, rustc_middle::ty::ParamTy>,
- ) -> Self {
- Self { tcx, params }
- }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseParamsSubstitutor<'tcx> {
- fn interner(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
- match *t.kind() {
- ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, bound }) => {
- match self.params.get(&bound.var.as_u32()) {
- Some(&ty::ParamTy { index, name }) => self.tcx.mk_ty_param(index, name),
- None => t,
- }
- }
-
- _ => t.super_fold_with(self),
- }
- }
-}
-
-/// Used to collect `Placeholder`s.
-pub(crate) struct PlaceholdersCollector {
- universe_index: ty::UniverseIndex,
- pub(crate) next_ty_placeholder: usize,
- pub(crate) next_anon_region_placeholder: u32,
-}
-
-impl PlaceholdersCollector {
- pub(crate) fn new() -> Self {
- PlaceholdersCollector {
- universe_index: ty::UniverseIndex::ROOT,
- next_ty_placeholder: 0,
- next_anon_region_placeholder: 0,
- }
- }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlaceholdersCollector {
- fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- match t.kind() {
- ty::Placeholder(p) if p.universe == self.universe_index => {
- self.next_ty_placeholder = self.next_ty_placeholder.max(p.bound.var.as_usize() + 1);
- }
-
- _ => (),
- };
-
- t.super_visit_with(self)
- }
-
- fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match *r {
- ty::RePlaceholder(p) if p.universe == self.universe_index => {
- if let ty::BoundRegionKind::BrAnon(_) = p.bound.kind {
- self.next_anon_region_placeholder =
- self.next_anon_region_placeholder.max(p.bound.var.as_u32());
- }
- // FIXME: This doesn't seem to handle BrNamed at all?
- }
-
- _ => (),
- };
-
- ControlFlow::Continue(())
- }
-}
diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs
deleted file mode 100644
index 8834449c9..000000000
--- a/compiler/rustc_traits/src/chalk/mod.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-//! Calls `chalk-solve` to solve a `ty::Predicate`
-//!
-//! In order to call `chalk-solve`, this file must convert a `CanonicalChalkEnvironmentAndGoal` into
-//! a Chalk uncanonical goal. It then calls Chalk, and converts the answer back into rustc solution.
-
-pub(crate) mod db;
-pub(crate) mod lowering;
-
-use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
-use rustc_middle::query::Providers;
-use rustc_middle::traits::ChalkRustInterner;
-use rustc_middle::ty::{self, TyCtxt, TypeFoldable, TypeVisitable};
-
-use rustc_infer::infer::canonical::{
- Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
-};
-use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};
-
-use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
-use crate::chalk::lowering::LowerInto;
-use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReverseParamsSubstitutor};
-
-use chalk_solve::Solution;
-
-pub(crate) fn provide(p: &mut Providers) {
- *p = Providers { evaluate_goal, ..*p };
-}
-
-pub(crate) fn evaluate_goal<'tcx>(
- tcx: TyCtxt<'tcx>,
- obligation: CanonicalChalkEnvironmentAndGoal<'tcx>,
-) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, traits::query::NoSolution> {
- let interner = ChalkRustInterner { tcx };
-
- // Chalk doesn't have a notion of `Params`, so instead we use placeholders.
- let mut placeholders_collector = PlaceholdersCollector::new();
- obligation.visit_with(&mut placeholders_collector);
-
- let mut params_substitutor =
- ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
- let obligation = obligation.fold_with(&mut params_substitutor);
- let params = params_substitutor.params;
-
- let max_universe = obligation.max_universe.index();
-
- let lowered_goal: chalk_ir::UCanonical<
- chalk_ir::InEnvironment<chalk_ir::Goal<ChalkRustInterner<'tcx>>>,
- > = chalk_ir::UCanonical {
- canonical: chalk_ir::Canonical {
- binders: chalk_ir::CanonicalVarKinds::from_iter(
- interner,
- obligation.variables.iter().map(|v| match v.kind {
- CanonicalVarKind::PlaceholderTy(_ty) => unimplemented!(),
- CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(),
- CanonicalVarKind::Ty(ty) => match ty {
- CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new(
- 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::TyVariableKind::Integer),
- chalk_ir::UniverseIndex::root(),
- ),
- CanonicalTyVarKind::Float => chalk_ir::WithKind::new(
- chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Float),
- chalk_ir::UniverseIndex::root(),
- ),
- },
- CanonicalVarKind::Region(ui) => chalk_ir::WithKind::new(
- chalk_ir::VariableKind::Lifetime,
- chalk_ir::UniverseIndex { counter: ui.index() },
- ),
- CanonicalVarKind::Const(_ui, _ty) => unimplemented!(),
- CanonicalVarKind::PlaceholderConst(_pc, _ty) => unimplemented!(),
- }),
- ),
- value: obligation.value.lower_into(interner),
- },
- universes: max_universe + 1,
- };
-
- use chalk_solve::Solver;
- let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
- let db = ChalkRustIrDatabase { interner };
- debug!(?lowered_goal);
- let solution = solver.solve(&db, &lowered_goal);
- debug!(?obligation, ?solution, "evaluate goal");
-
- // Ideally, the code to convert *back* to rustc types would live close to
- // the code to convert *from* rustc types. Right now though, we don't
- // really need this and so it's really minimal.
- // Right now, we also treat a `Unique` solution the same as
- // `Ambig(Definite)`. This really isn't right.
- let make_solution = |subst: chalk_ir::Substitution<_>,
- binders: chalk_ir::CanonicalVarKinds<_>| {
- use rustc_middle::infer::canonical::CanonicalVarInfo;
-
- let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params);
- let var_values = tcx.mk_substs_from_iter(
- subst
- .as_slice(interner)
- .iter()
- .map(|p| p.lower_into(interner).fold_with(&mut reverse_param_substitutor)),
- );
- let variables = binders.iter(interner).map(|var| {
- let kind = match var.kind {
- chalk_ir::VariableKind::Ty(ty_kind) => CanonicalVarKind::Ty(match ty_kind {
- chalk_ir::TyVariableKind::General => CanonicalTyVarKind::General(
- ty::UniverseIndex::from_usize(var.skip_kind().counter),
- ),
- chalk_ir::TyVariableKind::Integer => CanonicalTyVarKind::Int,
- chalk_ir::TyVariableKind::Float => CanonicalTyVarKind::Float,
- }),
- chalk_ir::VariableKind::Lifetime => {
- CanonicalVarKind::Region(ty::UniverseIndex::from_usize(var.skip_kind().counter))
- }
- // FIXME(compiler-errors): We don't currently have a way of turning
- // a Chalk ty back into a rustc ty, right?
- chalk_ir::VariableKind::Const(_) => todo!(),
- };
- CanonicalVarInfo { kind }
- });
- let max_universe = binders.iter(interner).map(|v| v.skip_kind().counter).max().unwrap_or(0);
- let sol = Canonical {
- max_universe: ty::UniverseIndex::from_usize(max_universe),
- variables: tcx.mk_canonical_var_infos_from_iter(variables),
- value: QueryResponse {
- var_values: CanonicalVarValues { var_values },
- region_constraints: QueryRegionConstraints::default(),
- certainty: Certainty::Proven,
- opaque_types: vec![],
- value: (),
- },
- };
- tcx.arena.alloc(sol)
- };
- solution
- .map(|s| match s {
- Solution::Unique(subst) => {
- // FIXME(chalk): handle constraints
- make_solution(subst.value.subst, subst.binders)
- }
- Solution::Ambig(guidance) => {
- match guidance {
- chalk_solve::Guidance::Definite(subst) => {
- make_solution(subst.value, subst.binders)
- }
- chalk_solve::Guidance::Suggested(_) => unimplemented!(),
- chalk_solve::Guidance::Unknown => {
- // chalk_fulfill doesn't use the var_values here, so
- // let's just ignore that
- let sol = Canonical {
- max_universe: ty::UniverseIndex::from_usize(0),
- variables: obligation.variables,
- value: QueryResponse {
- var_values: CanonicalVarValues::dummy(),
- region_constraints: QueryRegionConstraints::default(),
- certainty: Certainty::Ambiguous,
- opaque_types: vec![],
- value: (),
- },
- };
- &*tcx.arena.alloc(sol)
- }
- }
- }
- })
- .ok_or(traits::query::NoSolution)
-}
diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs
index ddba03b0b..2cd1c3b50 100644
--- a/compiler/rustc_traits/src/codegen.rs
+++ b/compiler/rustc_traits/src/codegen.rs
@@ -5,7 +5,7 @@
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _};
-use rustc_middle::traits::{CodegenObligationError, DefiningAnchor};
+use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::{
@@ -22,20 +22,14 @@ use rustc_trait_selection::traits::{
/// This also expects that `trait_ref` is fully normalized.
pub fn codegen_select_candidate<'tcx>(
tcx: TyCtxt<'tcx>,
- (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
+ (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::TraitRef<'tcx>),
) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> {
// We expect the input to be fully normalized.
debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref));
// Do the initial selection for the obligation. This yields the
// shallow result we are looking for -- that is, what specific impl.
- let infcx = tcx
- .infer_ctxt()
- .ignoring_regions()
- .with_opaque_type_inference(DefiningAnchor::Bubble)
- .build();
- //~^ HACK `Bubble` is required for
- // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
+ let infcx = tcx.infer_ctxt().ignoring_regions().build();
let mut selcx = SelectionContext::new(&infcx);
let obligation_cause = ObligationCause::dummy();
@@ -55,7 +49,7 @@ pub fn codegen_select_candidate<'tcx>(
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
- let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
+ let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(&infcx);
let impl_source = selection.map(|predicate| {
fulfill_cx.register_predicate_obligation(&infcx, predicate);
});
@@ -79,10 +73,5 @@ pub fn codegen_select_candidate<'tcx>(
let impl_source = infcx.resolve_vars_if_possible(impl_source);
let impl_source = infcx.tcx.erase_regions(impl_source);
- // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
- // as they will get constrained elsewhere, too.
- // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
- let _ = infcx.take_opaque_types();
-
Ok(&*tcx.arena.alloc(impl_source))
}
diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs
index f5b2753b7..e8917d724 100644
--- a/compiler/rustc_traits/src/evaluate_obligation.rs
+++ b/compiler/rustc_traits/src/evaluate_obligation.rs
@@ -1,6 +1,5 @@
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
-use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
use rustc_trait_selection::traits::query::CanonicalPredicateGoal;
@@ -16,14 +15,10 @@ fn evaluate_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalPredicateGoal<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
- assert!(!tcx.trait_solver_next());
+ assert!(!tcx.next_trait_solver_globally());
debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
- // HACK This bubble is required for this tests to pass:
- // impl-trait/issue99642.rs
- let (ref infcx, goal, _canonical_inference_vars) = tcx
- .infer_ctxt()
- .with_opaque_type_inference(DefiningAnchor::Bubble)
- .build_with_canonical(DUMMY_SP, &canonical_goal);
+ let (ref infcx, goal, _canonical_inference_vars) =
+ tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
debug!("evaluate_obligation: goal={:#?}", goal);
let ParamEnvAnd { param_env, value: predicate } = goal;
diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs
index 907e2d39c..dfa581421 100644
--- a/compiler/rustc_traits/src/lib.rs
+++ b/compiler/rustc_traits/src/lib.rs
@@ -1,10 +1,8 @@
-//! New recursive solver modeled on Chalk's recursive solver. Most of
-//! the guts are broken up into modules; see the comments in those modules.
+//! Queries that are independent from the main solver code.
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![feature(let_chains)]
-#![feature(drain_filter)]
#![recursion_limit = "256"]
#[macro_use]
@@ -12,7 +10,6 @@ extern crate tracing;
#[macro_use]
extern crate rustc_middle;
-mod chalk;
mod codegen;
mod dropck_outlives;
mod evaluate_obligation;
@@ -30,7 +27,6 @@ pub fn provide(p: &mut Providers) {
dropck_outlives::provide(p);
evaluate_obligation::provide(p);
implied_outlives_bounds::provide(p);
- chalk::provide(p);
normalize_projection_ty::provide(p);
normalize_erasing_regions::provide(p);
type_op::provide(p);
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index 94c33efae..2563e3ed1 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -56,20 +56,19 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<TyCtxt<'tcx>> + Par
fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
match p.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => false,
- ty::PredicateKind::Clause(ty::Clause::Trait(..))
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
- | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..))
+ ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false,
+ ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::AliasRelate(..)
- | ty::PredicateKind::WellFormed(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::ClosureKind(..)
| ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
- | ty::PredicateKind::ConstEvaluatable(..)
+ | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Ambiguous
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => true,
+ | ty::PredicateKind::Ambiguous => true,
}
}
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index b552ba41a..83828f177 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -10,7 +10,12 @@ use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
use std::sync::atomic::Ordering;
pub(crate) fn provide(p: &mut Providers) {
- *p = Providers { normalize_projection_ty, normalize_inherent_projection_ty, ..*p };
+ *p = Providers {
+ normalize_projection_ty,
+ normalize_weak_ty,
+ normalize_inherent_projection_ty,
+ ..*p
+ };
}
fn normalize_projection_ty<'tcx>(
@@ -43,6 +48,33 @@ fn normalize_projection_ty<'tcx>(
)
}
+fn normalize_weak_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ goal: CanonicalProjectionGoal<'tcx>,
+) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
+ debug!("normalize_provider(goal={:#?})", goal);
+
+ tcx.sess.perf_stats.normalize_projection_ty.fetch_add(1, Ordering::Relaxed);
+ tcx.infer_ctxt().enter_canonical_trait_query(
+ &goal,
+ |ocx, ParamEnvAnd { param_env, value: goal }| {
+ let obligations = tcx.predicates_of(goal.def_id).instantiate_own(tcx, goal.substs).map(
+ |(predicate, span)| {
+ traits::Obligation::new(
+ tcx,
+ ObligationCause::dummy_with_span(span),
+ param_env,
+ predicate,
+ )
+ },
+ );
+ ocx.register_obligations(obligations);
+ let normalized_ty = tcx.type_of(goal.def_id).subst(tcx, goal.substs);
+ Ok(NormalizationResult { normalized_ty })
+ },
+ )
+}
+
fn normalize_inherent_projection_ty<'tcx>(
tcx: TyCtxt<'tcx>,
goal: CanonicalProjectionGoal<'tcx>,
diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs
index 9904acb1c..01b9a5640 100644
--- a/compiler/rustc_traits/src/type_op.rs
+++ b/compiler/rustc_traits/src/type_op.rs
@@ -2,9 +2,8 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::DefiningAnchor;
+use rustc_middle::ty::{Clause, ParamEnvAnd};
use rustc_middle::ty::{FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable};
-use rustc_middle::ty::{ParamEnvAnd, Predicate};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{
@@ -24,7 +23,7 @@ pub(crate) fn provide(p: &mut Providers) {
type_op_prove_predicate,
type_op_subtype,
type_op_normalize_ty,
- type_op_normalize_predicate,
+ type_op_normalize_clause,
type_op_normalize_fn_sig,
type_op_normalize_poly_fn_sig,
..*p
@@ -71,10 +70,10 @@ fn type_op_normalize_ty<'tcx>(
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize)
}
-fn type_op_normalize_predicate<'tcx>(
+fn type_op_normalize_clause<'tcx>(
tcx: TyCtxt<'tcx>,
- canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Predicate<'tcx>>>>,
-) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Predicate<'tcx>>>, NoSolution> {
+ canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Clause<'tcx>>>>,
+) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, Clause<'tcx>>>, NoSolution> {
tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, type_op_normalize)
}
@@ -106,15 +105,10 @@ fn type_op_prove_predicate<'tcx>(
tcx: TyCtxt<'tcx>,
canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, ProvePredicate<'tcx>>>,
) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> {
- // HACK This bubble is required for this test to pass:
- // impl-trait/issue-99642.rs
- tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_canonical_trait_query(
- &canonicalized,
- |ocx, key| {
- type_op_prove_predicate_with_cause(ocx, key, ObligationCause::dummy());
- Ok(())
- },
- )
+ tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| {
+ type_op_prove_predicate_with_cause(ocx, key, ObligationCause::dummy());
+ Ok(())
+ })
}
/// The core of the `type_op_prove_predicate` query: for diagnostics purposes in NLL HRTB errors,
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index f8d05bc89..76d97e0e6 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -30,33 +30,49 @@ impl fmt::Debug for Byte {
}
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
-pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {}
+pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
+ fn min_align(&self) -> usize;
+
+ fn is_mutable(&self) -> bool;
+}
impl Def for ! {}
-impl Ref for ! {}
+impl Ref for ! {
+ fn min_align(&self) -> usize {
+ unreachable!()
+ }
+ fn is_mutable(&self) -> bool {
+ unreachable!()
+ }
+}
#[cfg(feature = "rustc")]
-pub(crate) mod rustc {
+pub mod rustc {
use rustc_middle::mir::Mutability;
- use rustc_middle::ty;
- use rustc_middle::ty::Region;
- use rustc_middle::ty::Ty;
+ use rustc_middle::ty::{self, Ty};
/// A reference in the layout.
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
pub struct Ref<'tcx> {
- lifetime: Region<'tcx>,
- ty: Ty<'tcx>,
- mutability: Mutability,
+ pub lifetime: ty::Region<'tcx>,
+ pub ty: Ty<'tcx>,
+ pub mutability: Mutability,
+ pub align: usize,
}
- impl<'tcx> super::Ref for Ref<'tcx> {}
+ impl<'tcx> super::Ref for Ref<'tcx> {
+ fn min_align(&self) -> usize {
+ self.align
+ }
- impl<'tcx> Ref<'tcx> {
- pub fn min_align(&self) -> usize {
- todo!()
+ fn is_mutable(&self) -> bool {
+ match self.mutability {
+ Mutability::Mut => true,
+ Mutability::Not => false,
+ }
}
}
+ impl<'tcx> Ref<'tcx> {}
/// A visibility node in the layout.
#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index a6d88b134..be434eb7d 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -188,14 +188,14 @@ pub(crate) mod rustc {
/// The layout of the type is unspecified.
Unspecified,
/// This error will be surfaced elsewhere by rustc, so don't surface it.
- Unknown,
+ UnknownLayout,
TypeError(ErrorGuaranteed),
}
- impl<'tcx> From<LayoutError<'tcx>> for Err {
- fn from(err: LayoutError<'tcx>) -> Self {
+ impl<'tcx> From<&LayoutError<'tcx>> for Err {
+ fn from(err: &LayoutError<'tcx>) -> Self {
match err {
- LayoutError::Unknown(..) => Self::Unknown,
+ LayoutError::Unknown(..) => Self::UnknownLayout,
err => unimplemented!("{:?}", err),
}
}
@@ -221,7 +221,7 @@ pub(crate) mod rustc {
}
impl LayoutSummary {
- fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, LayoutError<'tcx>> {
+ fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::{TyAndLayout, Variants};
@@ -365,6 +365,17 @@ pub(crate) mod rustc {
}
}))
}
+
+ ty::Ref(lifetime, ty, mutability) => {
+ let align = layout_of(tcx, *ty)?.align();
+ Ok(Tree::Ref(Ref {
+ lifetime: *lifetime,
+ ty: *ty,
+ mutability: *mutability,
+ align,
+ }))
+ }
+
_ => Err(Err::Unspecified),
}
}
@@ -471,7 +482,7 @@ pub(crate) mod rustc {
fn layout_of<'tcx>(
ctx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
- ) -> Result<alloc::Layout, LayoutError<'tcx>> {
+ ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
use rustc_middle::ty::ParamEnvAnd;
use rustc_target::abi::TyAndLayout;
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 77c0526e3..34ad6bd8c 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -8,7 +8,7 @@ extern crate tracing;
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
-pub(crate) mod layout;
+pub mod layout;
pub(crate) mod maybe_transmutable;
#[derive(Default)]
@@ -19,29 +19,29 @@ pub struct Assume {
pub validity: bool,
}
-/// The type encodes answers to the question: "Are these types transmutable?"
-#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
-pub enum Answer<R>
-where
- R: layout::Ref,
-{
- /// `Src` is transmutable into `Dst`.
+/// Either we have an error, transmutation is allowed, or we have an optional
+/// Condition that must hold.
+#[derive(Debug, Hash, Eq, PartialEq, Clone)]
+pub enum Answer<R> {
Yes,
-
- /// `Src` is NOT transmutable into `Dst`.
No(Reason),
+ If(Condition<R>),
+}
+/// A condition which must hold for safe transmutation to be possible.
+#[derive(Debug, Hash, Eq, PartialEq, Clone)]
+pub enum Condition<R> {
/// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
IfTransmutable { src: R, dst: R },
/// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
- IfAll(Vec<Answer<R>>),
+ IfAll(Vec<Condition<R>>),
/// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
- IfAny(Vec<Answer<R>>),
+ IfAny(Vec<Condition<R>>),
}
-/// Answers: Why wasn't the source type transmutable into the destination type?
+/// Answers "why wasn't the source type transmutable into the destination type?"
#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
pub enum Reason {
/// The layout of the source type is unspecified.
@@ -54,6 +54,16 @@ pub enum Reason {
DstIsPrivate,
/// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
DstIsTooBig,
+ /// Src should have a stricter alignment than Dst, but it does not.
+ DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
+ /// Can't go from shared pointer to unique pointer
+ DstIsMoreUnique,
+ /// Encountered a type error
+ TypeError,
+ /// The layout of src is unknown
+ SrcLayoutUnknown,
+ /// The layout of dst is unknown
+ DstLayoutUnknown,
}
#[cfg(feature = "rustc")]
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index 2e2fb90e7..b223a90f7 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -1,13 +1,13 @@
-use crate::Map;
-use crate::{Answer, Reason};
-
+pub(crate) mod query_context;
#[cfg(test)]
mod tests;
-mod query_context;
-use query_context::QueryContext;
+use crate::{
+ layout::{self, dfa, Byte, Dfa, Nfa, Ref, Tree, Uninhabited},
+ maybe_transmutable::query_context::QueryContext,
+ Answer, Condition, Map, Reason,
+};
-use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
pub(crate) struct MaybeTransmutableQuery<L, C>
where
C: QueryContext,
@@ -33,6 +33,7 @@ where
Self { src, dst, scope, assume, context }
}
+ // FIXME(bryangarza): Delete this when all usages are removed
pub(crate) fn map_layouts<F, M>(
self,
f: F,
@@ -53,6 +54,7 @@ where
}
}
+// FIXME: Nix this cfg, so we can write unit tests independently of rustc
#[cfg(feature = "rustc")]
mod rustc {
use super::*;
@@ -66,30 +68,26 @@ mod rustc {
/// then computes an answer using those trees.
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
- let query_or_answer = self.map_layouts(|src, dst, scope, &context| {
- // Convert `src` and `dst` from their rustc representations, to `Tree`-based
- // representations. If these conversions fail, conclude that the transmutation is
- // unacceptable; the layouts of both the source and destination types must be
- // well-defined.
- let src = Tree::from_ty(src, context);
- let dst = Tree::from_ty(dst, context);
-
- match (src, dst) {
- // Answer `Yes` here, because 'unknown layout' and type errors will already
- // be reported by rustc. No need to spam the user with more errors.
- (Err(Err::TypeError(_)), _) => Err(Answer::Yes),
- (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
- (Err(Err::Unknown), _) => Err(Answer::Yes),
- (_, Err(Err::Unknown)) => Err(Answer::Yes),
- (Err(Err::Unspecified), _) => Err(Answer::No(Reason::SrcIsUnspecified)),
- (_, Err(Err::Unspecified)) => Err(Answer::No(Reason::DstIsUnspecified)),
- (Ok(src), Ok(dst)) => Ok((src, dst)),
- }
- });
+ let Self { src, dst, scope, assume, context } = self;
+
+ // Convert `src` and `dst` from their rustc representations, to `Tree`-based
+ // representations. If these conversions fail, conclude that the transmutation is
+ // unacceptable; the layouts of both the source and destination types must be
+ // well-defined.
+ let src = Tree::from_ty(src, context);
+ let dst = Tree::from_ty(dst, context);
- match query_or_answer {
- Ok(query) => query.answer(),
- Err(answer) => answer,
+ match (src, dst) {
+ (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
+ Answer::No(Reason::TypeError)
+ }
+ (Err(Err::UnknownLayout), _) => Answer::No(Reason::SrcLayoutUnknown),
+ (_, Err(Err::UnknownLayout)) => Answer::No(Reason::DstLayoutUnknown),
+ (Err(Err::Unspecified), _) => Answer::No(Reason::SrcIsUnspecified),
+ (_, Err(Err::Unspecified)) => Answer::No(Reason::DstIsUnspecified),
+ (Ok(src), Ok(dst)) => {
+ MaybeTransmutableQuery { src, dst, scope, assume, context }.answer()
+ }
}
}
}
@@ -107,6 +105,7 @@ where
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
let assume_visibility = self.assume.safety;
+ // FIXME(bryangarza): Refactor this code to get rid of `map_layouts`
let query_or_answer = self.map_layouts(|src, dst, scope, context| {
// Remove all `Def` nodes from `src`, without checking their visibility.
let src = src.prune(&|def| true);
@@ -155,6 +154,7 @@ where
#[inline(always)]
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+ // FIXME(bryangarza): Refactor this code to get rid of `map_layouts`
let query_or_answer = self
.map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst))));
@@ -203,8 +203,29 @@ where
if let Some(answer) = cache.get(&(src_state, dst_state)) {
answer.clone()
} else {
+ debug!(?src_state, ?dst_state);
+ debug!(src = ?self.src);
+ debug!(dst = ?self.dst);
+ debug!(
+ src_transitions_len = self.src.transitions.len(),
+ dst_transitions_len = self.dst.transitions.len()
+ );
let answer = if dst_state == self.dst.accepting {
// truncation: `size_of(Src) >= size_of(Dst)`
+ //
+ // Why is truncation OK to do? Because even though the Src is bigger, all we care about
+ // is whether we have enough data for the Dst to be valid in accordance with what its
+ // type dictates.
+ // For example, in a u8 to `()` transmutation, we have enough data available from the u8
+ // to transmute it to a `()` (though in this case does `()` really need any data to
+ // begin with? It doesn't). Same thing with u8 to fieldless struct.
+ // Now then, why is something like u8 to bool not allowed? That is not because the bool
+ // is smaller in size, but rather because those 2 bits that we are re-interpreting from
+ // the u8 could introduce invalid states for the bool type.
+ //
+ // So, if it's possible to transmute to a smaller Dst by truncating, and we can guarantee
+ // that none of the actually-used data can introduce an invalid state for Dst's type, we
+ // are able to safely transmute, even with truncation.
Answer::Yes
} else if src_state == self.src.accepting {
// extension: `size_of(Src) >= size_of(Dst)`
@@ -214,108 +235,201 @@ where
Answer::No(Reason::DstIsTooBig)
}
} else {
- let src_quantification = if self.assume.validity {
+ let src_quantifier = if self.assume.validity {
// if the compiler may assume that the programmer is doing additional validity checks,
// (e.g.: that `src != 3u8` when the destination type is `bool`)
// then there must exist at least one transition out of `src_state` such that the transmute is viable...
- there_exists
+ Quantifier::ThereExists
} else {
// if the compiler cannot assume that the programmer is doing additional validity checks,
// then for all transitions out of `src_state`, such that the transmute is viable...
- // then there must exist at least one transition out of `src_state` such that the transmute is viable...
- for_all
+ // then there must exist at least one transition out of `dst_state` such that the transmute is viable...
+ Quantifier::ForAll
+ };
+
+ let bytes_answer = src_quantifier.apply(
+ // for each of the byte transitions out of the `src_state`...
+ self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+ |(&src_validity, &src_state_prime)| {
+ // ...try to find a matching transition out of `dst_state`.
+ if let Some(dst_state_prime) =
+ self.dst.byte_from(dst_state, src_validity)
+ {
+ self.answer_memo(cache, src_state_prime, dst_state_prime)
+ } else if let Some(dst_state_prime) =
+ // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
+ // (any init byte is a valid uninit byte)
+ self.dst.byte_from(dst_state, Byte::Uninit)
+ {
+ self.answer_memo(cache, src_state_prime, dst_state_prime)
+ } else {
+ // otherwise, we've exhausted our options.
+ // the DFAs, from this point onwards, are bit-incompatible.
+ Answer::No(Reason::DstIsBitIncompatible)
+ }
+ },
+ ),
+ );
+
+ // The below early returns reflect how this code would behave:
+ // if self.assume.validity {
+ // or(bytes_answer, refs_answer)
+ // } else {
+ // and(bytes_answer, refs_answer)
+ // }
+ // ...if `refs_answer` was computed lazily. The below early
+ // returns can be deleted without impacting the correctness of
+ // the algoritm; only its performance.
+ debug!(?bytes_answer);
+ match bytes_answer {
+ Answer::No(_) if !self.assume.validity => return bytes_answer,
+ Answer::Yes if self.assume.validity => return bytes_answer,
+ _ => {}
};
- src_quantification(
- self.src.bytes_from(src_state).unwrap_or(&Map::default()),
- |(&src_validity, &src_state_prime)| {
- if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) {
- self.answer_memo(cache, src_state_prime, dst_state_prime)
- } else if let Some(dst_state_prime) =
- self.dst.byte_from(dst_state, Byte::Uninit)
- {
- self.answer_memo(cache, src_state_prime, dst_state_prime)
- } else {
- Answer::No(Reason::DstIsBitIncompatible)
- }
- },
- )
+ let refs_answer = src_quantifier.apply(
+ // for each reference transition out of `src_state`...
+ self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+ |(&src_ref, &src_state_prime)| {
+ // ...there exists a reference transition out of `dst_state`...
+ Quantifier::ThereExists.apply(
+ self.dst
+ .refs_from(dst_state)
+ .unwrap_or(&Map::default())
+ .into_iter()
+ .map(|(&dst_ref, &dst_state_prime)| {
+ if !src_ref.is_mutable() && dst_ref.is_mutable() {
+ Answer::No(Reason::DstIsMoreUnique)
+ } else if !self.assume.alignment
+ && src_ref.min_align() < dst_ref.min_align()
+ {
+ Answer::No(Reason::DstHasStricterAlignment {
+ src_min_align: src_ref.min_align(),
+ dst_min_align: dst_ref.min_align(),
+ })
+ } else {
+ // ...such that `src` is transmutable into `dst`, if
+ // `src_ref` is transmutability into `dst_ref`.
+ and(
+ Answer::If(Condition::IfTransmutable {
+ src: src_ref,
+ dst: dst_ref,
+ }),
+ self.answer_memo(
+ cache,
+ src_state_prime,
+ dst_state_prime,
+ ),
+ )
+ }
+ }),
+ )
+ },
+ ),
+ );
+
+ if self.assume.validity {
+ or(bytes_answer, refs_answer)
+ } else {
+ and(bytes_answer, refs_answer)
+ }
};
- cache.insert((src_state, dst_state), answer.clone());
+ if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) {
+ panic!("failed to correctly cache transmutability")
+ }
answer
}
}
}
-impl<R> Answer<R>
+fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
- R: layout::Ref,
+ R: PartialEq,
{
- pub(crate) fn and(self, rhs: Self) -> Self {
- match (self, rhs) {
- (Self::No(reason), _) | (_, Self::No(reason)) => Self::No(reason),
- (Self::Yes, Self::Yes) => Self::Yes,
- (Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
- lhs.append(rhs);
- Self::IfAll(lhs)
- }
- (constraint, Self::IfAll(mut constraints))
- | (Self::IfAll(mut constraints), constraint) => {
- constraints.push(constraint);
- Self::IfAll(constraints)
- }
- (lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
+ match (lhs, rhs) {
+ // If both are errors, then we should return the more specific one
+ (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+ | (Answer::No(reason), Answer::No(_))
+ // If either is an error, return it
+ | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason),
+ // If only one side has a condition, pass it along
+ | (Answer::Yes, other) | (other, Answer::Yes) => other,
+ // If both sides have IfAll conditions, merge them
+ (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => {
+ lhs.append(rhs);
+ Answer::If(Condition::IfAll(lhs))
}
- }
-
- pub(crate) fn or(self, rhs: Self) -> Self {
- match (self, rhs) {
- (Self::Yes, _) | (_, Self::Yes) => Self::Yes,
- (Self::No(lhr), Self::No(rhr)) => Self::No(lhr),
- (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
- lhs.append(rhs);
- Self::IfAny(lhs)
- }
- (constraint, Self::IfAny(mut constraints))
- | (Self::IfAny(mut constraints), constraint) => {
- constraints.push(constraint);
- Self::IfAny(constraints)
- }
- (lhs, rhs) => Self::IfAny(vec![lhs, rhs]),
+ // If only one side is an IfAll, add the other Condition to it
+ (Answer::If(cond), Answer::If(Condition::IfAll(mut conds)))
+ | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => {
+ conds.push(cond);
+ Answer::If(Condition::IfAll(conds))
}
+ // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll
+ (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])),
}
}
-pub fn for_all<R, I, F>(iter: I, f: F) -> Answer<R>
+fn or<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
where
- R: layout::Ref,
- I: IntoIterator,
- F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
+ R: PartialEq,
{
- use std::ops::ControlFlow::{Break, Continue};
- let (Continue(result) | Break(result)) =
- iter.into_iter().map(f).try_fold(Answer::Yes, |constraints, constraint| {
- match constraint.and(constraints) {
- Answer::No(reason) => Break(Answer::No(reason)),
- maybe => Continue(maybe),
- }
- });
- result
+ match (lhs, rhs) {
+ // If both are errors, then we should return the more specific one
+ (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+ | (Answer::No(reason), Answer::No(_)) => Answer::No(reason),
+ // Otherwise, errors can be ignored for the rest of the pattern matching
+ (Answer::No(_), other) | (other, Answer::No(_)) => or(other, Answer::Yes),
+ // If only one side has a condition, pass it along
+ (Answer::Yes, other) | (other, Answer::Yes) => other,
+ // If both sides have IfAny conditions, merge them
+ (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => {
+ lhs.append(rhs);
+ Answer::If(Condition::IfAny(lhs))
+ }
+ // If only one side is an IfAny, add the other Condition to it
+ (Answer::If(cond), Answer::If(Condition::IfAny(mut conds)))
+ | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => {
+ conds.push(cond);
+ Answer::If(Condition::IfAny(conds))
+ }
+ // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny
+ (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])),
+ }
}
-pub fn there_exists<R, I, F>(iter: I, f: F) -> Answer<R>
-where
- R: layout::Ref,
- I: IntoIterator,
- F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
-{
- use std::ops::ControlFlow::{Break, Continue};
- let (Continue(result) | Break(result)) = iter.into_iter().map(f).try_fold(
- Answer::No(Reason::DstIsBitIncompatible),
- |constraints, constraint| match constraint.or(constraints) {
- Answer::Yes => Break(Answer::Yes),
- maybe => Continue(maybe),
- },
- );
- result
+pub enum Quantifier {
+ ThereExists,
+ ForAll,
+}
+
+impl Quantifier {
+ pub fn apply<R, I>(&self, iter: I) -> Answer<R>
+ where
+ R: layout::Ref,
+ I: IntoIterator<Item = Answer<R>>,
+ {
+ use std::ops::ControlFlow::{Break, Continue};
+
+ let (init, try_fold_f): (_, fn(_, _) -> _) = match self {
+ Self::ThereExists => {
+ (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R>, next| {
+ match or(accum, next) {
+ Answer::Yes => Break(Answer::Yes),
+ maybe => Continue(maybe),
+ }
+ })
+ }
+ Self::ForAll => (Answer::Yes, |accum: Answer<R>, next| {
+ let answer = and(accum, next);
+ match answer {
+ Answer::No(_) => Break(answer),
+ maybe => Continue(maybe),
+ }
+ }),
+ };
+
+ let (Continue(result) | Break(result)) = iter.into_iter().try_fold(init, try_fold_f);
+ result
+ }
}
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
index a8675f4ae..e49bebf57 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
@@ -1,9 +1,11 @@
use super::query_context::test::{Def, UltraMinimal};
use crate::maybe_transmutable::MaybeTransmutableQuery;
-use crate::{layout, Answer, Reason};
+use crate::{layout, Reason};
use itertools::Itertools;
mod bool {
+ use crate::Answer;
+
use super::*;
#[test]
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index 51885c9b4..50dac3a37 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2021"
[dependencies]
tracing = "0.1"
+itertools = "0.10.1"
rustc_middle = { path = "../rustc_middle" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 15c191046..55484f5c7 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -64,7 +64,7 @@ fn fn_sig_for_fn_abi<'tcx>(
// 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]);
+ inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]);
sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output);
sig
});
@@ -81,7 +81,7 @@ fn fn_sig_for_fn_abi<'tcx>(
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BoundRegionKind::BrEnv,
};
- let env_region = tcx.mk_re_late_bound(ty::INNERMOST, br);
+ let env_region = ty::Region::new_late_bound(tcx, ty::INNERMOST, br);
let env_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
let sig = sig.skip_binder();
@@ -106,12 +106,13 @@ fn fn_sig_for_fn_abi<'tcx>(
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BoundRegionKind::BrEnv,
};
- let env_ty = tcx.mk_mut_ref(tcx.mk_re_late_bound(ty::INNERMOST, br), ty);
+ let env_ty =
+ Ty::new_mut_ref(tcx, ty::Region::new_late_bound(tcx, ty::INNERMOST, br), ty);
let pin_did = tcx.require_lang_item(LangItem::Pin, None);
let pin_adt_ref = tcx.adt_def(pin_did);
let pin_substs = tcx.mk_substs(&[env_ty.into()]);
- let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
+ let env_ty = Ty::new_adt(tcx, pin_adt_ref, pin_substs);
let sig = sig.skip_binder();
// The `FnSig` and the `ret_ty` here is for a generators main
@@ -123,7 +124,7 @@ fn fn_sig_for_fn_abi<'tcx>(
let poll_did = tcx.require_lang_item(LangItem::Poll, None);
let poll_adt_ref = tcx.adt_def(poll_did);
let poll_substs = tcx.mk_substs(&[sig.return_ty.into()]);
- let ret_ty = tcx.mk_adt(poll_adt_ref, poll_substs);
+ let ret_ty = Ty::new_adt(tcx, poll_adt_ref, poll_substs);
// We have to replace the `ResumeTy` that is used for type and borrow checking
// with `&mut Context<'_>` which is used in codegen.
@@ -137,7 +138,7 @@ fn fn_sig_for_fn_abi<'tcx>(
panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
};
}
- let context_mut_ref = tcx.mk_task_context();
+ let context_mut_ref = Ty::new_task_context(tcx);
(context_mut_ref, ret_ty)
} else {
@@ -145,7 +146,7 @@ fn fn_sig_for_fn_abi<'tcx>(
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.mk_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
- let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+ let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_substs);
(sig.resume_ty, ret_ty)
};
@@ -202,7 +203,7 @@ fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
fn fn_abi_of_fn_ptr<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
-) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
+) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
let (param_env, (sig, extra_args)) = query.into_parts();
let cx = LayoutCx { tcx, param_env };
@@ -212,7 +213,7 @@ fn fn_abi_of_fn_ptr<'tcx>(
fn fn_abi_of_instance<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>)>,
-) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
+) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
let (param_env, (instance, extra_args)) = query.into_parts();
let sig = fn_sig_for_fn_abi(tcx, instance, param_env);
@@ -331,7 +332,7 @@ fn fn_abi_new_uncached<'tcx>(
fn_def_id: Option<DefId>,
// FIXME(eddyb) replace this with something typed, like an `enum`.
force_thin_self_ptr: bool,
-) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, FnAbiError<'tcx>> {
+) -> Result<&'tcx FnAbi<'tcx, Ty<'tcx>>, &'tcx FnAbiError<'tcx>> {
let sig = cx.tcx.normalize_erasing_late_bound_regions(cx.param_env, sig);
let conv = conv_from_spec_abi(cx.tcx(), sig.abi);
@@ -376,7 +377,7 @@ fn fn_abi_new_uncached<'tcx>(
let is_drop_in_place =
fn_def_id.is_some() && fn_def_id == cx.tcx.lang_items().drop_in_place_fn();
- let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
+ let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> {
let span = tracing::debug_span!("arg_of");
let _entered = span.enter();
let is_return = arg_idx.is_none();
@@ -386,7 +387,8 @@ fn fn_abi_new_uncached<'tcx>(
_ => bug!("argument to drop_in_place is not a raw ptr: {:?}", ty),
});
- let layout = cx.layout_of(ty)?;
+ let layout =
+ cx.layout_of(ty).map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::Layout(*err)))?;
let layout = if force_thin_self_ptr && arg_idx == Some(0) {
// Don't pass the vtable, it's not an argument of the virtual fn.
// Instead, pass just the data pointer, but give it the type `*const/mut dyn Trait`
@@ -454,7 +456,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
fn_abi: &mut FnAbi<'tcx, Ty<'tcx>>,
abi: SpecAbi,
fn_def_id: Option<DefId>,
-) -> Result<(), FnAbiError<'tcx>> {
+) -> Result<(), &'tcx FnAbiError<'tcx>> {
if abi == SpecAbi::Unadjusted {
return Ok(());
}
@@ -548,7 +550,9 @@ fn fn_abi_adjust_for_abi<'tcx>(
fixup(arg, Some(arg_idx));
}
} else {
- fn_abi.adjust_for_foreign_abi(cx, abi)?;
+ fn_abi
+ .adjust_for_foreign_abi(cx, abi)
+ .map_err(|err| &*cx.tcx.arena.alloc(FnAbiError::AdjustForForeignAbi(err)))?;
}
Ok(())
@@ -563,7 +567,7 @@ fn make_thin_self_ptr<'tcx>(
let fat_pointer_ty = if layout.is_unsized() {
// unsized `self` is passed as a pointer to `self`
// FIXME (mikeyhew) change this to use &own if it is ever added to the language
- tcx.mk_mut_ptr(layout.ty)
+ Ty::new_mut_ptr(tcx, layout.ty)
} else {
match layout.abi {
Abi::ScalarPair(..) | Abi::Scalar(..) => (),
@@ -597,7 +601,7 @@ fn make_thin_self_ptr<'tcx>(
// we now have a type like `*mut RcBox<dyn Trait>`
// change its layout to that of `*mut ()`, a thin pointer, but keep the same type
// this is understood as a special case elsewhere in the compiler
- let unit_ptr_ty = tcx.mk_mut_ptr(tcx.mk_unit());
+ let unit_ptr_ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx));
TyAndLayout {
ty: fat_pointer_ty,
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index ed574f22e..897e7aad4 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -5,7 +5,7 @@ use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::definitions::DefPathData;
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, InternalSubsts, Ty, TyCtxt};
use rustc_span::symbol::kw;
pub fn provide(providers: &mut Providers) {
@@ -259,7 +259,7 @@ fn associated_type_for_impl_trait_in_trait(
opaque_ty_def_id: LocalDefId,
) -> LocalDefId {
let (hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id)) =
- tcx.hir().expect_item(opaque_ty_def_id).expect_opaque_ty().origin
+ tcx.opaque_type_origin(opaque_ty_def_id)
else {
bug!("expected opaque for {opaque_ty_def_id:?}");
};
@@ -297,11 +297,12 @@ fn associated_type_for_impl_trait_in_trait(
// Copy visility of the containing function.
trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
- // Copy impl_defaultness of the containing function.
- trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id));
+ // Copy defaultness of the containing function.
+ trait_assoc_ty.defaultness(tcx.defaultness(fn_def_id));
// Copy type_of of the opaque.
- trait_assoc_ty.type_of(ty::EarlyBinder(tcx.mk_opaque(
+ trait_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_opaque(
+ tcx,
opaque_ty_def_id.to_def_id(),
InternalSubsts::identity_for_item(tcx, opaque_ty_def_id),
)));
@@ -337,15 +338,10 @@ fn associated_type_for_impl_trait_in_trait(
param_def_id_to_index,
has_self: opaque_ty_generics.has_self,
has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
+ host_effect_index: parent_generics.host_effect_index,
}
});
- // There are no predicates for the synthesized associated type.
- trait_assoc_ty.explicit_predicates_of(ty::GenericPredicates {
- parent: Some(trait_def_id.to_def_id()),
- predicates: &[],
- });
-
// There are no inferred outlives for the synthesized associated type.
trait_assoc_ty.inferred_outlives_of(&[]);
@@ -393,8 +389,8 @@ fn associated_type_for_impl_trait_in_impl(
// Copy visility of the containing function.
impl_assoc_ty.visibility(tcx.visibility(impl_fn_def_id));
- // Copy impl_defaultness of the containing function.
- impl_assoc_ty.impl_defaultness(tcx.impl_defaultness(impl_fn_def_id));
+ // Copy defaultness of the containing function.
+ impl_assoc_ty.defaultness(tcx.defaultness(impl_fn_def_id));
// Copy generics_of the trait's associated item but the impl as the parent.
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty) resolves to the trait instead of the impl
@@ -421,15 +417,10 @@ fn associated_type_for_impl_trait_in_impl(
param_def_id_to_index,
has_self: false,
has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
+ host_effect_index: parent_generics.host_effect_index,
}
});
- // There are no predicates for the synthesized associated type.
- impl_assoc_ty.explicit_predicates_of(ty::GenericPredicates {
- parent: Some(impl_local_def_id.to_def_id()),
- predicates: &[],
- });
-
// There are no inferred outlives for the synthesized associated type.
impl_assoc_ty.inferred_outlives_of(&[]);
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 1219bb400..426c98012 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -33,8 +33,10 @@ pub(crate) fn destructure_const<'tcx>(
let (fields, variant) = match const_.ty().kind() {
ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
// construct the consts for the elements of the array/slice
- let field_consts =
- branches.iter().map(|b| tcx.mk_const(*b, *inner_ty)).collect::<Vec<_>>();
+ let field_consts = branches
+ .iter()
+ .map(|b| ty::Const::new_value(tcx, *b, *inner_ty))
+ .collect::<Vec<_>>();
debug!(?field_consts);
(field_consts, None)
@@ -52,7 +54,7 @@ pub(crate) fn destructure_const<'tcx>(
for (field, field_valtree) in iter::zip(fields, branches) {
let field_ty = field.ty(tcx, substs);
- let field_const = tcx.mk_const(*field_valtree, field_ty);
+ let field_const = ty::Const::new_value(tcx, *field_valtree, field_ty);
field_consts.push(field_const);
}
debug!(?field_consts);
@@ -61,7 +63,7 @@ pub(crate) fn destructure_const<'tcx>(
}
ty::Tuple(elem_tys) => {
let fields = iter::zip(*elem_tys, branches)
- .map(|(elem_ty, elem_valtree)| tcx.mk_const(*elem_valtree, elem_ty))
+ .map(|(elem_ty, elem_valtree)| ty::Const::new_value(tcx, *elem_valtree, elem_ty))
.collect::<Vec<_>>();
(fields, None)
@@ -78,8 +80,9 @@ pub(crate) fn destructure_const<'tcx>(
fn check_binop(op: mir::BinOp) -> bool {
use mir::BinOp::*;
match op {
- Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le | Ne
- | Ge | Gt => true,
+ Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Div | Rem | BitXor
+ | BitAnd | BitOr | Shl | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge
+ | Gt => true,
Offset => false,
}
}
@@ -116,7 +119,7 @@ fn recurse_build<'tcx>(
let sp = node.span;
match tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
Ok(c) => c,
- Err(LitToConstError::Reported(guar)) => tcx.const_error(node.ty, guar),
+ Err(LitToConstError::Reported(guar)) => ty::Const::new_error(tcx, guar, node.ty),
Err(LitToConstError::TypeError) => {
bug!("encountered type error in lit_to_const")
}
@@ -124,17 +127,17 @@ fn recurse_build<'tcx>(
}
&ExprKind::NonHirLiteral { lit, user_ty: _ } => {
let val = ty::ValTree::from_scalar_int(lit);
- tcx.mk_const(val, node.ty)
+ ty::Const::new_value(tcx, val, node.ty)
}
&ExprKind::ZstLiteral { user_ty: _ } => {
let val = ty::ValTree::zst();
- tcx.mk_const(val, node.ty)
+ ty::Const::new_value(tcx, val, node.ty)
}
&ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
let uneval = ty::UnevaluatedConst::new(def_id, substs);
- tcx.mk_const(uneval, node.ty)
+ ty::Const::new_unevaluated(tcx, uneval, node.ty)
}
- ExprKind::ConstParam { param, .. } => tcx.mk_const(*param, node.ty),
+ ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param, node.ty),
ExprKind::Call { fun, args, .. } => {
let fun = recurse_build(tcx, body, *fun, root_span)?;
@@ -144,16 +147,16 @@ fn recurse_build<'tcx>(
new_args.push(recurse_build(tcx, body, id, root_span)?);
}
let new_args = tcx.mk_const_list(&new_args);
- tcx.mk_const(Expr::FunctionCall(fun, new_args), node.ty)
+ ty::Const::new_expr(tcx, Expr::FunctionCall(fun, new_args), node.ty)
}
&ExprKind::Binary { op, lhs, rhs } if check_binop(op) => {
let lhs = recurse_build(tcx, body, lhs, root_span)?;
let rhs = recurse_build(tcx, body, rhs, root_span)?;
- tcx.mk_const(Expr::Binop(op, lhs, rhs), node.ty)
+ ty::Const::new_expr(tcx, Expr::Binop(op, lhs, rhs), node.ty)
}
&ExprKind::Unary { op, arg } if check_unop(op) => {
let arg = recurse_build(tcx, body, arg, root_span)?;
- tcx.mk_const(Expr::UnOp(op, arg), node.ty)
+ ty::Const::new_expr(tcx, Expr::UnOp(op, arg), node.ty)
}
// This is necessary so that the following compiles:
//
@@ -174,11 +177,11 @@ fn recurse_build<'tcx>(
// This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
&ExprKind::Use { source } => {
let arg = recurse_build(tcx, body, source, root_span)?;
- tcx.mk_const(Expr::Cast(CastKind::Use, arg, node.ty), node.ty)
+ ty::Const::new_expr(tcx, Expr::Cast(CastKind::Use, arg, node.ty), node.ty)
}
&ExprKind::Cast { source } => {
let arg = recurse_build(tcx, body, source, root_span)?;
- tcx.mk_const(Expr::Cast(CastKind::As, arg, node.ty), node.ty)
+ ty::Const::new_expr(tcx, Expr::Cast(CastKind::As, arg, node.ty), node.ty)
}
ExprKind::Borrow { arg, .. } => {
let arg_node = &body.exprs[*arg];
@@ -218,7 +221,7 @@ fn recurse_build<'tcx>(
maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?
}
// dont know if this is correct
- ExprKind::Pointer { .. } => {
+ ExprKind::PointerCoercion { .. } => {
error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
}
ExprKind::Yield { .. } => {
@@ -240,7 +243,8 @@ fn recurse_build<'tcx>(
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
}
- ExprKind::Closure { .. } | ExprKind::Return { .. } => {
+ // FIXME(explicit_tail_calls): maybe get `become` a new error
+ ExprKind::Closure { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => {
error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
}
// let expressions imply control flow
@@ -320,7 +324,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::Cast { .. }
| thir::ExprKind::Use { .. }
| thir::ExprKind::NeverToAny { .. }
- | thir::ExprKind::Pointer { .. }
+ | thir::ExprKind::PointerCoercion { .. }
| thir::ExprKind::Loop { .. }
| thir::ExprKind::Let { .. }
| thir::ExprKind::Match { .. }
@@ -336,6 +340,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
| thir::ExprKind::Break { .. }
| thir::ExprKind::Continue { .. }
| thir::ExprKind::Return { .. }
+ | thir::ExprKind::Become { .. }
| thir::ExprKind::Array { .. }
| thir::ExprKind::Tuple { .. }
| thir::ExprKind::Adt(_)
@@ -419,7 +424,7 @@ pub fn thir_abstract_const(
let root_span = body.exprs[body_id].span;
- Ok(Some(ty::EarlyBinder(recurse_build(tcx, body, body_id, root_span)?)))
+ Ok(Some(ty::EarlyBinder::bind(recurse_build(tcx, body, body_id, root_span)?)))
}
pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs
index 553bf40ef..947d4bbe8 100644
--- a/compiler/rustc_ty_utils/src/errors.rs
+++ b/compiler/rustc_ty_utils/src/errors.rs
@@ -113,7 +113,7 @@ pub struct DuplicateArg<'tcx> {
}
#[derive(Diagnostic)]
-#[diag(ty_utils_impl_trait_not_param)]
+#[diag(ty_utils_impl_trait_not_param, code = "E0792")]
pub struct NotParam<'tcx> {
pub arg: GenericArg<'tcx>,
#[primary_span]
diff --git a/compiler/rustc_ty_utils/src/implied_bounds.rs b/compiler/rustc_ty_utils/src/implied_bounds.rs
index 081be0658..10dec9a7a 100644
--- a/compiler/rustc_ty_utils/src/implied_bounds.rs
+++ b/compiler/rustc_ty_utils/src/implied_bounds.rs
@@ -1,45 +1,56 @@
-use rustc_hir::{def::DefKind, def_id::DefId};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::LocalDefId;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::iter;
pub fn provide(providers: &mut Providers) {
*providers = Providers { assumed_wf_types, ..*providers };
}
-fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
+fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'tcx>, Span)] {
match tcx.def_kind(def_id) {
DefKind::Fn => {
let sig = tcx.fn_sig(def_id).subst_identity();
- let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
- liberated_sig.inputs_and_output
+ let liberated_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
+ tcx.arena.alloc_from_iter(itertools::zip_eq(
+ liberated_sig.inputs_and_output,
+ fn_sig_spans(tcx, def_id),
+ ))
}
DefKind::AssocFn => {
let sig = tcx.fn_sig(def_id).subst_identity();
- let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
+ let liberated_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
let mut assumed_wf_types: Vec<_> =
- tcx.assumed_wf_types(tcx.parent(def_id)).as_slice().into();
- assumed_wf_types.extend(liberated_sig.inputs_and_output);
- tcx.mk_type_list(&assumed_wf_types)
+ tcx.assumed_wf_types(tcx.local_parent(def_id)).into();
+ assumed_wf_types.extend(itertools::zip_eq(
+ liberated_sig.inputs_and_output,
+ fn_sig_spans(tcx, def_id),
+ ));
+ tcx.arena.alloc_slice(&assumed_wf_types)
}
DefKind::Impl { .. } => {
- match tcx.impl_trait_ref(def_id) {
- Some(trait_ref) => {
- let types: Vec<_> = trait_ref.skip_binder().substs.types().collect();
- tcx.mk_type_list(&types)
- }
- // Only the impl self type
- None => tcx.mk_type_list(&[tcx.type_of(def_id).subst_identity()]),
- }
+ // Trait arguments and the self type for trait impls or only the self type for
+ // inherent impls.
+ let tys = match tcx.impl_trait_ref(def_id) {
+ Some(trait_ref) => trait_ref.skip_binder().substs.types().collect(),
+ None => vec![tcx.type_of(def_id).subst_identity()],
+ };
+
+ let mut impl_spans = impl_spans(tcx, def_id);
+ tcx.arena.alloc_from_iter(tys.into_iter().map(|ty| (ty, impl_spans.next().unwrap())))
}
- DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
- DefKind::OpaqueTy => match tcx.def_kind(tcx.parent(def_id)) {
+ DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
+ DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
DefKind::TyAlias => ty::List::empty(),
- DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
+ DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
// Nested opaque types only occur in associated types:
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
// assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
// and `&'static T`.
- DefKind::OpaqueTy => bug!("unimplemented implied bounds for neseted opaque types"),
+ DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
def_kind @ _ => {
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
}
@@ -72,3 +83,28 @@ fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
| DefKind::Generator => ty::List::empty(),
}
}
+
+fn fn_sig_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator<Item = Span> + '_ {
+ let node = tcx.hir().get(tcx.local_def_id_to_hir_id(def_id));
+ if let Some(decl) = node.fn_decl() {
+ decl.inputs.iter().map(|ty| ty.span).chain(iter::once(decl.output.span()))
+ } else {
+ bug!("unexpected item for fn {def_id:?}: {node:?}")
+ }
+}
+
+fn impl_spans(tcx: TyCtxt<'_>, def_id: LocalDefId) -> impl Iterator<Item = Span> + '_ {
+ let item = tcx.hir().expect_item(def_id);
+ if let hir::ItemKind::Impl(impl_) = item.kind {
+ let trait_args = impl_
+ .of_trait
+ .into_iter()
+ .flat_map(|trait_ref| trait_ref.path.segments.last().unwrap().args().args)
+ .map(|arg| arg.span());
+ let dummy_spans_for_default_args =
+ impl_.of_trait.into_iter().flat_map(|trait_ref| iter::repeat(trait_ref.path.span));
+ iter::once(impl_.self_ty.span).chain(trait_args).chain(dummy_spans_for_default_args)
+ } else {
+ bug!("unexpected item for impl {def_id:?}: {item:?}")
+ }
+}
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 36a20c78f..1d93a79e5 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -80,7 +80,7 @@ fn resolve_associated_item<'tcx>(
let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
- let vtbl = match tcx.codegen_select_candidate((param_env, ty::Binder::dummy(trait_ref))) {
+ let vtbl = match tcx.codegen_select_candidate((param_env, trait_ref)) {
Ok(vtbl) => vtbl,
Err(CodegenObligationError::Ambiguity) => {
let reported = tcx.sess.delay_span_bug(
@@ -177,85 +177,6 @@ fn resolve_associated_item<'tcx>(
Some(ty::Instance::new(leaf_def.item.def_id, substs))
}
- traits::ImplSource::Generator(generator_data) => {
- if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::resume {
- // For compiler developers who'd like to add new items to `Generator`,
- // you either need to generate a shim body, or perhaps return
- // `InstanceDef::Item` pointing to a trait default method body if
- // it is given a default implementation by the trait.
- span_bug!(
- tcx.def_span(generator_data.generator_def_id),
- "no definition for `{trait_ref}::{}` for built-in generator type",
- tcx.item_name(trait_item_id)
- )
- }
- Some(Instance {
- def: ty::InstanceDef::Item(generator_data.generator_def_id),
- substs: generator_data.substs,
- })
- }
- traits::ImplSource::Future(future_data) => {
- if Some(trait_item_id) == tcx.lang_items().future_poll_fn() {
- // `Future::poll` is generated by the compiler.
- Some(Instance {
- def: ty::InstanceDef::Item(future_data.generator_def_id),
- substs: future_data.substs,
- })
- } else {
- // All other methods are default methods of the `Future` trait.
- // (this assumes that `ImplSource::Future` is only used for methods on `Future`)
- debug_assert!(tcx.impl_defaultness(trait_item_id).has_value());
- Some(Instance::new(trait_item_id, rcvr_substs))
- }
- }
- traits::ImplSource::Closure(closure_data) => {
- if cfg!(debug_assertions)
- && ![sym::call, sym::call_mut, sym::call_once]
- .contains(&tcx.item_name(trait_item_id))
- {
- // For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
- // you either need to generate a shim body, or perhaps return
- // `InstanceDef::Item` pointing to a trait default method body if
- // it is given a default implementation by the trait.
- span_bug!(
- tcx.def_span(closure_data.closure_def_id),
- "no definition for `{trait_ref}::{}` for built-in closure type",
- tcx.item_name(trait_item_id)
- )
- }
- let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
- Instance::resolve_closure(
- tcx,
- closure_data.closure_def_id,
- closure_data.substs,
- trait_closure_kind,
- )
- }
- traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() {
- ty::FnDef(..) | ty::FnPtr(..) => {
- if cfg!(debug_assertions)
- && ![sym::call, sym::call_mut, sym::call_once]
- .contains(&tcx.item_name(trait_item_id))
- {
- // For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
- // you either need to generate a shim body, or perhaps return
- // `InstanceDef::Item` pointing to a trait default method body if
- // it is given a default implementation by the trait.
- bug!(
- "no definition for `{trait_ref}::{}` for built-in fn type",
- tcx.item_name(trait_item_id)
- )
- }
- Some(Instance {
- def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
- substs: rcvr_substs,
- })
- }
- _ => bug!(
- "no built-in definition for `{trait_ref}::{}` for non-fn type",
- tcx.item_name(trait_item_id)
- ),
- },
traits::ImplSource::Object(ref data) => {
traits::get_vtable_index_of_object_method(tcx, data, trait_item_id).map(|index| {
Instance {
@@ -308,15 +229,71 @@ fn resolve_associated_item<'tcx>(
span: tcx.def_span(trait_item_id),
})
}
+ } else if Some(trait_ref.def_id) == lang_items.future_trait() {
+ let ty::Generator(generator_def_id, substs, _) = *rcvr_substs.type_at(0).kind() else {
+ bug!()
+ };
+ if Some(trait_item_id) == tcx.lang_items().future_poll_fn() {
+ // `Future::poll` is generated by the compiler.
+ Some(Instance { def: ty::InstanceDef::Item(generator_def_id), substs: substs })
+ } else {
+ // All other methods are default methods of the `Future` trait.
+ // (this assumes that `ImplSource::Builtin` is only used for methods on `Future`)
+ debug_assert!(tcx.defaultness(trait_item_id).has_value());
+ Some(Instance::new(trait_item_id, rcvr_substs))
+ }
+ } else if Some(trait_ref.def_id) == lang_items.gen_trait() {
+ let ty::Generator(generator_def_id, substs, _) = *rcvr_substs.type_at(0).kind() else {
+ bug!()
+ };
+ if cfg!(debug_assertions) && tcx.item_name(trait_item_id) != sym::resume {
+ // For compiler developers who'd like to add new items to `Generator`,
+ // you either need to generate a shim body, or perhaps return
+ // `InstanceDef::Item` pointing to a trait default method body if
+ // it is given a default implementation by the trait.
+ span_bug!(
+ tcx.def_span(generator_def_id),
+ "no definition for `{trait_ref}::{}` for built-in generator type",
+ tcx.item_name(trait_item_id)
+ )
+ }
+ Some(Instance { def: ty::InstanceDef::Item(generator_def_id), substs })
+ } else if tcx.fn_trait_kind_from_def_id(trait_ref.def_id).is_some() {
+ // FIXME: This doesn't check for malformed libcore that defines, e.g.,
+ // `trait Fn { fn call_once(&self) { .. } }`. This is mostly for extension
+ // methods.
+ if cfg!(debug_assertions)
+ && ![sym::call, sym::call_mut, sym::call_once]
+ .contains(&tcx.item_name(trait_item_id))
+ {
+ // For compiler developers who'd like to add new items to `Fn`/`FnMut`/`FnOnce`,
+ // you either need to generate a shim body, or perhaps return
+ // `InstanceDef::Item` pointing to a trait default method body if
+ // it is given a default implementation by the trait.
+ bug!(
+ "no definition for `{trait_ref}::{}` for built-in callable type",
+ tcx.item_name(trait_item_id)
+ )
+ }
+ match *rcvr_substs.type_at(0).kind() {
+ ty::Closure(closure_def_id, substs) => {
+ let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
+ Instance::resolve_closure(tcx, closure_def_id, substs, trait_closure_kind)
+ }
+ ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
+ def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_substs.type_at(0)),
+ substs: rcvr_substs,
+ }),
+ _ => bug!(
+ "no built-in definition for `{trait_ref}::{}` for non-fn type",
+ tcx.item_name(trait_item_id)
+ ),
+ }
} else {
None
}
}
- traits::ImplSource::AutoImpl(..)
- | traits::ImplSource::Param(..)
- | traits::ImplSource::TraitAlias(..)
- | traits::ImplSource::TraitUpcasting(_)
- | traits::ImplSource::ConstDestruct(_) => None,
+ traits::ImplSource::Param(..) | traits::ImplSource::TraitUpcasting(_) => None,
})
}
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 16cd8bc8e..b67cd96a7 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -31,7 +31,7 @@ pub fn provide(providers: &mut Providers) {
fn layout_of<'tcx>(
tcx: TyCtxt<'tcx>,
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
+) -> Result<TyAndLayout<'tcx>, &'tcx LayoutError<'tcx>> {
let (param_env, ty) = query.into_parts();
debug!(?ty);
@@ -45,7 +45,9 @@ fn layout_of<'tcx>(
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
Ok(t) => t,
Err(normalization_error) => {
- return Err(LayoutError::NormalizationFailure(ty, normalization_error));
+ return Err(tcx
+ .arena
+ .alloc(LayoutError::NormalizationFailure(ty, normalization_error)));
}
};
@@ -66,27 +68,34 @@ fn layout_of<'tcx>(
Ok(layout)
}
+fn error<'tcx>(
+ cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
+ err: LayoutError<'tcx>,
+) -> &'tcx LayoutError<'tcx> {
+ cx.tcx.arena.alloc(err)
+}
+
fn univariant_uninterned<'tcx>(
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
ty: Ty<'tcx>,
fields: &IndexSlice<FieldIdx, Layout<'_>>,
repr: &ReprOptions,
kind: StructKind,
-) -> Result<LayoutS, LayoutError<'tcx>> {
+) -> Result<LayoutS, &'tcx LayoutError<'tcx>> {
let dl = cx.data_layout();
let pack = repr.pack;
if pack.is_some() && repr.align.is_some() {
cx.tcx.sess.delay_span_bug(DUMMY_SP, "struct cannot be packed and aligned");
- return Err(LayoutError::Unknown(ty));
+ return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
}
- cx.univariant(dl, fields, repr, kind).ok_or(LayoutError::SizeOverflow(ty))
+ cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
}
fn layout_of_uncached<'tcx>(
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
ty: Ty<'tcx>,
-) -> Result<Layout<'tcx>, LayoutError<'tcx>> {
+) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
let tcx = cx.tcx;
let param_env = cx.param_env;
let dl = cx.data_layout();
@@ -145,17 +154,35 @@ fn layout_of_uncached<'tcx>(
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
}
- let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
-
let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type()
// Projection eagerly bails out when the pointee references errors,
// fall back to structurally deducing metadata.
&& !pointee.references_error()
{
- let metadata_ty = tcx.normalize_erasing_regions(
+ let pointee_metadata = Ty::new_projection(tcx,metadata_def_id, [pointee]);
+ let metadata_ty = match tcx.try_normalize_erasing_regions(
param_env,
- tcx.mk_projection(metadata_def_id, [pointee]),
- );
+ pointee_metadata,
+ ) {
+ Ok(metadata_ty) => metadata_ty,
+ Err(mut err) => {
+ // Usually `<Ty as Pointee>::Metadata` can't be normalized because
+ // its struct tail cannot be normalized either, so try to get a
+ // more descriptive layout error here, which will lead to less confusing
+ // diagnostics.
+ match tcx.try_normalize_erasing_regions(
+ param_env,
+ tcx.struct_tail_without_normalization(pointee),
+ ) {
+ Ok(_) => {},
+ Err(better_err) => {
+ err = better_err;
+ }
+ }
+ return Err(error(cx, LayoutError::NormalizationFailure(pointee, err)));
+ },
+ };
+
let metadata_layout = cx.layout_of(metadata_ty)?;
// If the metadata is a 1-zst, then the pointer is thin.
if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 {
@@ -163,10 +190,13 @@ fn layout_of_uncached<'tcx>(
}
let Abi::Scalar(metadata) = metadata_layout.abi else {
- return Err(LayoutError::Unknown(unsized_part));
+ return Err(error(cx, LayoutError::Unknown(pointee)));
};
+
metadata
} else {
+ let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env);
+
match unsized_part.kind() {
ty::Foreign(..) => {
return Ok(tcx.mk_layout(LayoutS::scalar(cx, data_ptr)));
@@ -178,7 +208,7 @@ fn layout_of_uncached<'tcx>(
vtable
}
_ => {
- return Err(LayoutError::Unknown(unsized_part));
+ return Err(error(cx, LayoutError::Unknown(pointee)));
}
}
};
@@ -200,14 +230,18 @@ fn layout_of_uncached<'tcx>(
if count.has_projections() {
count = tcx.normalize_erasing_regions(param_env, count);
if count.has_projections() {
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
}
- let count =
- count.try_eval_target_usize(tcx, param_env).ok_or(LayoutError::Unknown(ty))?;
+ let count = count
+ .try_eval_target_usize(tcx, param_env)
+ .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
let element = cx.layout_of(element)?;
- let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow(ty))?;
+ let size = element
+ .size
+ .checked_mul(count, dl)
+ .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
let abi = if count != 0 && ty.is_privately_uninhabited(tcx, param_env) {
Abi::Uninhabited
@@ -295,7 +329,7 @@ fn layout_of_uncached<'tcx>(
DUMMY_SP,
"#[repr(simd)] was applied to an ADT that is not a struct",
);
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
let fields = &def.non_enum_variant().fields;
@@ -325,7 +359,7 @@ fn layout_of_uncached<'tcx>(
DUMMY_SP,
"#[repr(simd)] was applied to an ADT with heterogeneous field type",
);
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
}
@@ -347,7 +381,7 @@ fn layout_of_uncached<'tcx>(
// Extract the number of elements from the layout of the array field:
let FieldsShape::Array { count, .. } = cx.layout_of(f0_ty)?.layout.fields() else {
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
};
(*e_ty, *count, true)
@@ -376,7 +410,10 @@ fn layout_of_uncached<'tcx>(
};
// Compute the size and alignment of the vector:
- let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow(ty))?;
+ let size = e_ly
+ .size
+ .checked_mul(e_len, dl)
+ .ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))?;
let align = dl.vector_align(size);
let size = size.align_to(align.abi);
@@ -417,53 +454,101 @@ fn layout_of_uncached<'tcx>(
tcx.def_span(def.did()),
"union cannot be packed and aligned",
);
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
return Ok(tcx.mk_layout(
- cx.layout_of_union(&def.repr(), &variants).ok_or(LayoutError::Unknown(ty))?,
+ cx.layout_of_union(&def.repr(), &variants)
+ .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?,
));
}
- tcx.mk_layout(
- cx.layout_of_struct_or_enum(
+ let get_discriminant_type =
+ |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max);
+
+ let discriminants_iter = || {
+ def.is_enum()
+ .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128)))
+ .into_iter()
+ .flatten()
+ };
+
+ let dont_niche_optimize_enum = def.repr().inhibit_enum_layout_opt()
+ || def
+ .variants()
+ .iter_enumerated()
+ .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()));
+
+ let maybe_unsized = def.is_struct()
+ && def.non_enum_variant().tail_opt().is_some_and(|last_field| {
+ let param_env = tcx.param_env(def.did());
+ !tcx.type_of(last_field.did).subst_identity().is_sized(tcx, param_env)
+ });
+
+ let Some(layout) = cx.layout_of_struct_or_enum(
+ &def.repr(),
+ &variants,
+ def.is_enum(),
+ def.is_unsafe_cell(),
+ tcx.layout_scalar_valid_range(def.did()),
+ get_discriminant_type,
+ discriminants_iter(),
+ dont_niche_optimize_enum,
+ !maybe_unsized,
+ ) else {
+ return Err(error(cx, LayoutError::SizeOverflow(ty)));
+ };
+
+ // If the struct tail is sized and can be unsized, check that unsizing doesn't move the fields around.
+ if cfg!(debug_assertions)
+ && maybe_unsized
+ && def.non_enum_variant().tail().ty(tcx, substs).is_sized(tcx, cx.param_env)
+ {
+ let mut variants = variants;
+ let tail_replacement = cx.layout_of(Ty::new_slice(tcx, tcx.types.u8)).unwrap();
+ *variants[FIRST_VARIANT].raw.last_mut().unwrap() = tail_replacement.layout;
+
+ let Some(unsized_layout) = cx.layout_of_struct_or_enum(
&def.repr(),
&variants,
def.is_enum(),
def.is_unsafe_cell(),
tcx.layout_scalar_valid_range(def.did()),
- |min, max| Integer::repr_discr(tcx, ty, &def.repr(), min, max),
- def.is_enum()
- .then(|| def.discriminants(tcx).map(|(v, d)| (v, d.val as i128)))
- .into_iter()
- .flatten(),
- def.repr().inhibit_enum_layout_opt()
- || def
- .variants()
- .iter_enumerated()
- .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())),
- {
- let param_env = tcx.param_env(def.did());
- def.is_struct()
- && match def.variants().iter().next().and_then(|x| x.fields.raw.last())
- {
- Some(last_field) => tcx
- .type_of(last_field.did)
- .subst_identity()
- .is_sized(tcx, param_env),
- None => false,
- }
- },
- )
- .ok_or(LayoutError::SizeOverflow(ty))?,
- )
+ get_discriminant_type,
+ discriminants_iter(),
+ dont_niche_optimize_enum,
+ !maybe_unsized,
+ ) else {
+ bug!("failed to compute unsized layout of {ty:?}");
+ };
+
+ let FieldsShape::Arbitrary { offsets: sized_offsets, .. } = &layout.fields else {
+ bug!("unexpected FieldsShape for sized layout of {ty:?}: {:?}", layout.fields);
+ };
+ let FieldsShape::Arbitrary { offsets: unsized_offsets, .. } = &unsized_layout.fields else {
+ bug!("unexpected FieldsShape for unsized layout of {ty:?}: {:?}", unsized_layout.fields);
+ };
+
+ let (sized_tail, sized_fields) = sized_offsets.raw.split_last().unwrap();
+ let (unsized_tail, unsized_fields) = unsized_offsets.raw.split_last().unwrap();
+
+ if sized_fields != unsized_fields {
+ bug!("unsizing {ty:?} changed field order!\n{layout:?}\n{unsized_layout:?}");
+ }
+
+ if sized_tail < unsized_tail {
+ bug!("unsizing {ty:?} moved tail backwards!\n{layout:?}\n{unsized_layout:?}");
+ }
+ }
+
+ tcx.mk_layout(layout)
}
// Types with no meaningful known layout.
ty::Alias(..) => {
// NOTE(eddyb) `layout_of` query should've normalized these away,
// if that was possible, so there's no reason to try again here.
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
ty::Bound(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Infer(_) => {
@@ -471,7 +556,7 @@ fn layout_of_uncached<'tcx>(
}
ty::Placeholder(..) | ty::Param(_) | ty::Error(_) => {
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
}
})
}
@@ -607,13 +692,13 @@ fn generator_layout<'tcx>(
ty: Ty<'tcx>,
def_id: hir::def_id::DefId,
substs: SubstsRef<'tcx>,
-) -> Result<Layout<'tcx>, LayoutError<'tcx>> {
+) -> Result<Layout<'tcx>, &'tcx LayoutError<'tcx>> {
use SavedLocalEligibility::*;
let tcx = cx.tcx;
- let subst_field = |ty: Ty<'tcx>| EarlyBinder(ty).subst(tcx, substs);
+ let subst_field = |ty: Ty<'tcx>| EarlyBinder::bind(ty).subst(tcx, substs);
let Some(info) = tcx.generator_layout(def_id) else {
- return Err(LayoutError::Unknown(ty));
+ return Err(error(cx, LayoutError::Unknown(ty)));
};
let (ineligible_locals, assignments) = generator_saved_local_eligibility(&info);
@@ -634,7 +719,7 @@ fn generator_layout<'tcx>(
let promoted_layouts = ineligible_locals
.iter()
.map(|local| subst_field(info.field_tys[local].ty))
- .map(|ty| tcx.mk_maybe_uninit(ty))
+ .map(|ty| Ty::new_maybe_uninit(tcx, ty))
.map(|ty| Ok(cx.layout_of(ty)?.layout));
let prefix_layouts = substs
.as_generator()
@@ -944,7 +1029,7 @@ fn variant_info_for_generator<'tcx>(
return (vec![], None);
};
- let (generator, state_specific_names) = cx.tcx.generator_layout_and_saved_local_names(def_id);
+ let generator = cx.tcx.optimized_mir(def_id).generator_layout().unwrap();
let upvar_names = cx.tcx.closure_saved_names_of_captured_variables(def_id);
let mut upvars_size = Size::ZERO;
@@ -959,7 +1044,7 @@ fn variant_info_for_generator<'tcx>(
upvars_size = upvars_size.max(offset + field_layout.size);
FieldInfo {
kind: FieldKind::Upvar,
- name: Symbol::intern(&name),
+ name: *name,
offset: offset.bytes(),
size: field_layout.size.bytes(),
align: field_layout.align.abi.bytes(),
@@ -983,9 +1068,10 @@ fn variant_info_for_generator<'tcx>(
variant_size = variant_size.max(offset + field_layout.size);
FieldInfo {
kind: FieldKind::GeneratorLocal,
- name: state_specific_names.get(*local).copied().flatten().unwrap_or(
- Symbol::intern(&format!(".generator_field{}", local.as_usize())),
- ),
+ name: generator.field_names[*local].unwrap_or(Symbol::intern(&format!(
+ ".generator_field{}",
+ local.as_usize()
+ ))),
offset: offset.bytes(),
size: field_layout.size.bytes(),
align: field_layout.align.abi.bytes(),
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 1f9701b93..9d593dc5e 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -210,7 +210,7 @@ fn drop_tys_helper<'tcx>(
match subty.kind() {
ty::Adt(adt_id, subst) => {
for subty in tcx.adt_drop_tys(adt_id.did())? {
- vec.push(EarlyBinder(subty).subst(tcx, subst));
+ vec.push(EarlyBinder::bind(subty).subst(tcx, subst));
}
}
_ => vec.push(subty),
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 4e91dd380..570c3b245 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -1,12 +1,13 @@
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::ErrorGuaranteed;
+use rustc_hir::intravisit::Visitor;
use rustc_hir::{def::DefKind, def_id::LocalDefId};
+use rustc_hir::{intravisit, CRATE_HIR_ID};
use rustc_middle::query::Providers;
use rustc_middle::ty::util::{CheckRegions, NotUniqueParam};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_span::Span;
-use rustc_type_ir::AliasKind;
+use rustc_trait_selection::traits::check_substs_compatible;
use std::ops::ControlFlow;
use crate::errors::{DuplicateArg, NotParam};
@@ -19,26 +20,40 @@ struct OpaqueTypeCollector<'tcx> {
/// Avoid infinite recursion due to recursive declarations.
seen: FxHashSet<LocalDefId>,
+
+ span: Option<Span>,
}
impl<'tcx> OpaqueTypeCollector<'tcx> {
- fn collect(
- tcx: TyCtxt<'tcx>,
- item: LocalDefId,
- val: ty::Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>,
- ) -> Vec<LocalDefId> {
- let mut collector = Self { tcx, opaques: Vec::new(), item, seen: Default::default() };
- val.skip_binder().visit_with(&mut collector);
- collector.opaques
+ fn new(tcx: TyCtxt<'tcx>, item: LocalDefId) -> Self {
+ Self { tcx, opaques: Vec::new(), item, seen: Default::default(), span: None }
}
fn span(&self) -> Span {
- self.tcx.def_span(self.item)
+ self.span.unwrap_or_else(|| {
+ self.tcx.def_ident_span(self.item).unwrap_or_else(|| self.tcx.def_span(self.item))
+ })
+ }
+
+ fn visit_spanned(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) {
+ let old = self.span;
+ self.span = Some(span);
+ value.visit_with(self);
+ self.span = old;
+ }
+
+ fn parent_trait_ref(&self) -> Option<ty::TraitRef<'tcx>> {
+ let parent = self.parent()?;
+ if matches!(self.tcx.def_kind(parent), DefKind::Impl { .. }) {
+ Some(self.tcx.impl_trait_ref(parent)?.subst_identity())
+ } else {
+ None
+ }
}
fn parent(&self) -> Option<LocalDefId> {
match self.tcx.def_kind(self.item) {
- DefKind::Fn => None,
+ DefKind::AnonConst | DefKind::InlineConst | DefKind::Fn | DefKind::TyAlias => None,
DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
Some(self.tcx.local_parent(self.item))
}
@@ -48,119 +63,245 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
),
}
}
+
+ /// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `self.item`.
+ ///
+ /// Example:
+ /// ```ignore UNSOLVED (is this a bug?)
+ /// # #![feature(type_alias_impl_trait)]
+ /// pub mod foo {
+ /// pub mod bar {
+ /// pub trait Bar { /* ... */ }
+ /// pub type Baz = impl Bar;
+ ///
+ /// # impl Bar for () {}
+ /// fn f1() -> Baz { /* ... */ }
+ /// }
+ /// fn f2() -> bar::Baz { /* ... */ }
+ /// }
+ /// ```
+ ///
+ /// and `opaque_def_id` is the `DefId` of the definition of the opaque type `Baz`.
+ /// For the above example, this function returns `true` for `f1` and `false` for `f2`.
+ #[instrument(level = "trace", skip(self), ret)]
+ fn check_tait_defining_scope(&self, opaque_def_id: LocalDefId) -> bool {
+ let mut hir_id = self.tcx.hir().local_def_id_to_hir_id(self.item);
+ let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(opaque_def_id);
+
+ // Named opaque types can be defined by any siblings or children of siblings.
+ let scope = self.tcx.hir().get_defining_scope(opaque_hir_id);
+ // We walk up the node tree until we hit the root or the scope of the opaque type.
+ while hir_id != scope && hir_id != CRATE_HIR_ID {
+ hir_id = self.tcx.hir().get_parent_item(hir_id).into();
+ }
+ // Syntactically, we are allowed to define the concrete type if:
+ hir_id == scope
+ }
+
+ fn collect_body_and_predicate_taits(&mut self) {
+ // Look at all where bounds.
+ self.tcx.predicates_of(self.item).instantiate_identity(self.tcx).visit_with(self);
+ // An item is allowed to constrain opaques declared within its own body (but not nested within
+ // nested functions).
+ self.collect_taits_declared_in_body();
+ }
+
+ #[instrument(level = "trace", skip(self))]
+ fn collect_taits_declared_in_body(&mut self) {
+ let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(self.item)).value;
+ struct TaitInBodyFinder<'a, 'tcx> {
+ collector: &'a mut OpaqueTypeCollector<'tcx>,
+ }
+ impl<'v> intravisit::Visitor<'v> for TaitInBodyFinder<'_, '_> {
+ #[instrument(level = "trace", skip(self))]
+ fn visit_nested_item(&mut self, id: rustc_hir::ItemId) {
+ let id = id.owner_id.def_id;
+ if let DefKind::TyAlias = self.collector.tcx.def_kind(id) {
+ let items = self.collector.tcx.opaque_types_defined_by(id);
+ self.collector.opaques.extend(items);
+ }
+ }
+ #[instrument(level = "trace", skip(self))]
+ // Recurse into these, as they are type checked with their parent
+ fn visit_nested_body(&mut self, id: rustc_hir::BodyId) {
+ let body = self.collector.tcx.hir().body(id);
+ self.visit_body(body);
+ }
+ }
+ TaitInBodyFinder { collector: self }.visit_expr(body);
+ }
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
- type BreakTy = ErrorGuaranteed;
-
#[instrument(skip(self), ret, level = "trace")]
- fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<ErrorGuaranteed> {
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
+ t.super_visit_with(self)?;
match t.kind() {
- ty::Alias(AliasKind::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
+ ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
if !self.seen.insert(alias_ty.def_id.expect_local()) {
return ControlFlow::Continue(());
}
+
+ // TAITs outside their defining scopes are ignored.
+ let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
+ trace!(?origin);
+ match origin {
+ rustc_hir::OpaqueTyOrigin::FnReturn(_)
+ | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
+ rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
+ if !in_assoc_ty {
+ if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
+ return ControlFlow::Continue(());
+ }
+ }
+ }
+ }
+
+ self.opaques.push(alias_ty.def_id.expect_local());
+
match self.tcx.uses_unique_generic_params(alias_ty.substs, CheckRegions::Bound) {
Ok(()) => {
// FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
// supported at all, so this is sound to do, but once we want to support them, you'll
// start seeing the error below.
- self.opaques.push(alias_ty.def_id.expect_local());
-
// Collect opaque types nested within the associated type bounds of this opaque type.
- for (pred, _span) in self
+ // We use identity substs here, because we already know that the opaque type uses
+ // only generic parameters, and thus substituting would not give us more information.
+ for (pred, span) in self
.tcx
.explicit_item_bounds(alias_ty.def_id)
- .subst_iter_copied(self.tcx, alias_ty.substs)
+ .subst_identity_iter_copied()
{
trace!(?pred);
- pred.visit_with(self)?;
+ self.visit_spanned(span, pred);
}
-
- ControlFlow::Continue(())
}
Err(NotUniqueParam::NotParam(arg)) => {
- let err = self.tcx.sess.emit_err(NotParam {
+ self.tcx.sess.emit_err(NotParam {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
- ControlFlow::Break(err)
}
Err(NotUniqueParam::DuplicateParam(arg)) => {
- let err = self.tcx.sess.emit_err(DuplicateArg {
+ self.tcx.sess.emit_err(DuplicateArg {
arg,
span: self.span(),
opaque_span: self.tcx.def_span(alias_ty.def_id),
});
- ControlFlow::Break(err)
}
}
}
- ty::Alias(AliasKind::Projection, alias_ty) => {
- if let Some(parent) = self.parent() {
- trace!(?alias_ty);
- let (trait_ref, own_substs) = alias_ty.trait_ref_and_own_substs(self.tcx);
-
- trace!(?trait_ref, ?own_substs);
- // This avoids having to do normalization of `Self::AssocTy` by only
- // supporting the case of a method defining opaque types from assoc types
- // in the same impl block.
- if trait_ref.self_ty() == self.tcx.type_of(parent).subst_identity() {
- for assoc in self.tcx.associated_items(parent).in_definition_order() {
+ ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
+ self.tcx
+ .type_of(alias_ty.def_id)
+ .subst(self.tcx, alias_ty.substs)
+ .visit_with(self)?;
+ }
+ ty::Alias(ty::Projection, alias_ty) => {
+ // This avoids having to do normalization of `Self::AssocTy` by only
+ // supporting the case of a method defining opaque types from assoc types
+ // in the same impl block.
+ if let Some(parent_trait_ref) = self.parent_trait_ref() {
+ // If the trait ref of the associated item and the impl differs,
+ // then we can't use the impl's identity substitutions below, so
+ // just skip.
+ if alias_ty.trait_ref(self.tcx) == parent_trait_ref {
+ let parent = self.parent().expect("we should have a parent here");
+
+ for &assoc in self.tcx.associated_items(parent).in_definition_order() {
trace!(?assoc);
- if assoc.trait_item_def_id == Some(alias_ty.def_id) {
- // We reconstruct the generic args of the associated type within the impl
- // from the impl's generics and the generic args passed to the type via the
- // projection.
- let substs = ty::InternalSubsts::identity_for_item(
- self.tcx,
- parent.to_def_id(),
- );
- trace!(?substs);
- let substs: Vec<_> =
- substs.iter().chain(own_substs.iter().copied()).collect();
- trace!(?substs);
- // Find opaque types in this associated type.
+ if assoc.trait_item_def_id != Some(alias_ty.def_id) {
+ continue;
+ }
+
+ // If the type is further specializable, then the type_of
+ // is not actually correct below.
+ if !assoc.defaultness(self.tcx).is_final() {
+ continue;
+ }
+
+ let impl_substs = alias_ty.substs.rebase_onto(
+ self.tcx,
+ parent_trait_ref.def_id,
+ ty::InternalSubsts::identity_for_item(self.tcx, parent),
+ );
+
+ if check_substs_compatible(self.tcx, assoc, impl_substs) {
return self
.tcx
.type_of(assoc.def_id)
- .subst(self.tcx, &substs)
+ .subst(self.tcx, impl_substs)
.visit_with(self);
+ } else {
+ self.tcx.sess.delay_span_bug(
+ self.tcx.def_span(assoc.def_id),
+ "item had incorrect substs",
+ );
}
}
}
}
- t.super_visit_with(self)
}
- _ => t.super_visit_with(self),
+ ty::Adt(def, _) if def.did().is_local() => {
+ if !self.seen.insert(def.did().expect_local()) {
+ return ControlFlow::Continue(());
+ }
+ for variant in def.variants().iter() {
+ for field in variant.fields.iter() {
+ // Don't use the `ty::Adt` substs, we either
+ // * found the opaque in the substs
+ // * will find the opaque in the unsubstituted fields
+ // The only other situation that can occur is that after substituting,
+ // some projection resolves to an opaque that we would have otherwise
+ // not found. While we could substitute and walk those, that would mean we
+ // would have to walk all substitutions of an Adt, which can quickly
+ // degenerate into looking at an exponential number of types.
+ let ty = self.tcx.type_of(field.did).subst_identity();
+ self.visit_spanned(self.tcx.def_span(field.did), ty);
+ }
+ }
+ }
+ _ => trace!(kind=?t.kind()),
}
+ ControlFlow::Continue(())
}
}
fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [LocalDefId] {
let kind = tcx.def_kind(item);
trace!(?kind);
- // FIXME(type_alias_impl_trait): This is definitely still wrong except for RPIT and impl trait in assoc types.
+ let mut collector = OpaqueTypeCollector::new(tcx, item);
match kind {
- // We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds`
- DefKind::Fn | DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => {
- let defined_opaques = match kind {
- DefKind::Fn => {
- OpaqueTypeCollector::collect(tcx, item, tcx.fn_sig(item).subst_identity())
- }
- DefKind::AssocFn => {
- OpaqueTypeCollector::collect(tcx, item, tcx.fn_sig(item).subst_identity())
- }
- DefKind::AssocTy | DefKind::AssocConst => OpaqueTypeCollector::collect(
- tcx,
- item,
- ty::Binder::dummy(tcx.type_of(item).subst_identity()),
- ),
- _ => unreachable!(),
+ // Walk over the signature of the function-like to find the opaques.
+ DefKind::AssocFn | DefKind::Fn => {
+ let ty_sig = tcx.fn_sig(item).subst_identity();
+ let hir_sig = tcx.hir().get_by_def_id(item).fn_sig().unwrap();
+ // Walk over the inputs and outputs manually in order to get good spans for them.
+ collector.visit_spanned(hir_sig.decl.output.span(), ty_sig.output());
+ for (hir, ty) in hir_sig.decl.inputs.iter().zip(ty_sig.inputs().iter()) {
+ collector.visit_spanned(hir.span, ty.map_bound(|x| *x));
+ }
+ collector.collect_body_and_predicate_taits();
+ }
+ // Walk over the type of the item to find opaques.
+ DefKind::Static(_) | DefKind::Const | DefKind::AssocConst | DefKind::AnonConst => {
+ let span = match tcx.hir().get_by_def_id(item).ty() {
+ Some(ty) => ty.span,
+ _ => tcx.def_span(item),
};
- tcx.arena.alloc_from_iter(defined_opaques)
+ collector.visit_spanned(span, tcx.type_of(item).subst_identity());
+ collector.collect_body_and_predicate_taits();
+ }
+ // We're also doing this for `AssocTy` for the wf checks in `check_opaque_meets_bounds`
+ DefKind::TyAlias | DefKind::AssocTy => {
+ tcx.type_of(item).subst_identity().visit_with(&mut collector);
+ }
+ DefKind::OpaqueTy => {
+ for (pred, span) in tcx.explicit_item_bounds(item).subst_identity_iter_copied() {
+ collector.visit_spanned(span, pred);
+ }
}
DefKind::Mod
| DefKind::Struct
@@ -168,29 +309,26 @@ fn opaque_types_defined_by<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx [
| DefKind::Enum
| DefKind::Variant
| DefKind::Trait
- | DefKind::TyAlias
| DefKind::ForeignTy
| DefKind::TraitAlias
| DefKind::TyParam
- | DefKind::Const
| DefKind::ConstParam
- | DefKind::Static(_)
| DefKind::Ctor(_, _)
| DefKind::Macro(_)
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
- | DefKind::AnonConst
- | DefKind::InlineConst
- | DefKind::OpaqueTy
| DefKind::ImplTraitPlaceholder
| DefKind::Field
| DefKind::LifetimeParam
| DefKind::GlobalAsm
- | DefKind::Impl { .. }
- | DefKind::Closure
- | DefKind::Generator => &[],
+ | DefKind::Impl { .. } => {}
+ // Closures and generators are type checked with their parent, so there is no difference here.
+ DefKind::Closure | DefKind::Generator | DefKind::InlineConst => {
+ return tcx.opaque_types_defined_by(tcx.local_parent(item));
+ }
}
+ tcx.arena.alloc_from_iter(collector.opaques)
}
pub(super) fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 65dc3c39c..6e5c50492 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -1,13 +1,11 @@
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_index::bit_set::BitSet;
use rustc_middle::query::Providers;
use rustc_middle::ty::{
- self, Binder, EarlyBinder, ImplTraitInTraitData, Predicate, PredicateKind, ToPredicate, Ty,
- TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+ self, EarlyBinder, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
-use rustc_session::config::TraitSolver;
use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits;
@@ -44,9 +42,7 @@ fn sized_constraint_for_ty<'tcx>(
let adt_tys = adt.sized_constraint(tcx);
debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
adt_tys
- .0
- .iter()
- .map(|ty| adt_tys.rebind(*ty).subst(tcx, substs))
+ .subst_iter_copied(tcx, substs)
.flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
.collect()
}
@@ -77,13 +73,13 @@ fn sized_constraint_for_ty<'tcx>(
result
}
-fn impl_defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
+fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
match tcx.hir().get_by_def_id(def_id) {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness,
hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
| hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness,
node => {
- bug!("`impl_defaultness` called on {:?}", node);
+ bug!("`defaultness` called on {:?}", node);
}
}
}
@@ -99,7 +95,7 @@ fn impl_defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] {
if let Some(def_id) = def_id.as_local() {
if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
- return tcx.mk_type_list(&[tcx.ty_error_misc()]);
+ return tcx.mk_type_list(&[Ty::new_misc_error(tcx)]);
}
}
let def = tcx.adt_def(def_id);
@@ -107,7 +103,7 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> &[Ty<'_>] {
let result = tcx.mk_type_list_from_iter(
def.variants()
.iter()
- .filter_map(|v| v.fields.raw.last())
+ .filter_map(|v| v.tail_opt())
.flat_map(|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did).subst_identity())),
);
@@ -122,20 +118,6 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
let ty::InstantiatedPredicates { mut predicates, .. } =
tcx.predicates_of(def_id).instantiate_identity(tcx);
- // When computing the param_env of an RPITIT, use predicates of the containing function,
- // *except* for the additional assumption that the RPITIT normalizes to the trait method's
- // default opaque type. This is needed to properly check the item bounds of the assoc
- // type hold (`check_type_bounds`), since that method already installs a similar projection
- // bound, so they will conflict.
- // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): I don't like this, we should
- // at least be making sure that the generics in RPITITs and their parent fn don't
- // get out of alignment, or else we do actually need to substitute these predicates.
- if let Some(ImplTraitInTraitData::Trait { fn_def_id, .. })
- | Some(ImplTraitInTraitData::Impl { fn_def_id, .. }) = tcx.opt_rpitit_info(def_id)
- {
- predicates = tcx.predicates_of(fn_def_id).instantiate_identity(tcx).predicates;
- }
-
// Finally, we have to normalize the bounds in the environment, in
// case they contain any associated type projections. This process
// can yield errors if the put in illegal associated types, like
@@ -148,11 +130,6 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
// are any errors at that point, so outside of type inference you can be
// sure that this will succeed without errors anyway.
- if tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Chalk {
- let environment = well_formed_types_in_env(tcx, def_id);
- predicates.extend(environment);
- }
-
if tcx.def_kind(def_id) == DefKind::AssocFn
&& tcx.associated_item(def_id).container == ty::AssocItemContainer::TraitContainer
{
@@ -188,6 +165,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
kind: hir::TraitItemKind::Const(..), ..
})
| hir::Node::AnonConst(_)
+ | hir::Node::ConstBlock(_)
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
| hir::Node::ImplItem(hir::ImplItem {
kind:
@@ -246,7 +224,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
};
let unnormalized_env =
- ty::ParamEnv::new(tcx.mk_predicates(&predicates), traits::Reveal::UserFacing, constness);
+ ty::ParamEnv::new(tcx.mk_clauses(&predicates), traits::Reveal::UserFacing, constness);
let body_id = local_did.unwrap_or(CRATE_DEF_ID);
let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id);
@@ -259,7 +237,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
/// its corresponding opaque within the body of a default-body trait method.
struct ImplTraitInTraitFinder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
- predicates: &'a mut Vec<Predicate<'tcx>>,
+ predicates: &'a mut Vec<ty::Clause<'tcx>>,
fn_def_id: DefId,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
seen: FxHashSet<DefId>,
@@ -289,12 +267,13 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
let shifted_alias_ty = self.tcx.fold_regions(unshifted_alias_ty, |re, depth| {
if let ty::ReLateBound(index, bv) = re.kind() {
if depth != ty::INNERMOST {
- return self.tcx.mk_re_error_with_message(
+ return ty::Region::new_error_with_message(
+ self.tcx,
DUMMY_SP,
"we shouldn't walk non-predicate binders with `impl Trait`...",
);
}
- self.tcx.mk_re_late_bound(index.shifted_out_to_binder(self.depth), bv)
+ ty::Region::new_late_bound(self.tcx, index.shifted_out_to_binder(self.depth), bv)
} else {
re
}
@@ -306,7 +285,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
let default_ty = if self.tcx.lower_impl_trait_in_trait_to_assoc_ty() {
self.tcx.type_of(shifted_alias_ty.def_id).subst(self.tcx, shifted_alias_ty.substs)
} else {
- self.tcx.mk_alias(ty::Opaque, shifted_alias_ty)
+ Ty::new_alias(self.tcx,ty::Opaque, shifted_alias_ty)
};
self.predicates.push(
@@ -334,118 +313,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
}
}
-/// Elaborate the environment.
-///
-/// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s
-/// that are assumed to be well-formed (because they come from the environment).
-///
-/// Used only in chalk mode.
-fn well_formed_types_in_env(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Predicate<'_>> {
- use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
- use rustc_middle::ty::subst::GenericArgKind;
-
- debug!("environment(def_id = {:?})", def_id);
-
- // The environment of an impl Trait type is its defining function's environment.
- if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
- return well_formed_types_in_env(tcx, parent.to_def_id());
- }
-
- // Compute the bounds on `Self` and the type parameters.
- let ty::InstantiatedPredicates { predicates, .. } =
- tcx.predicates_of(def_id).instantiate_identity(tcx);
-
- let clauses = predicates.into_iter();
-
- if !def_id.is_local() {
- return ty::List::empty();
- }
- let node = tcx.hir().get_by_def_id(def_id.expect_local());
-
- enum NodeKind {
- TraitImpl,
- InherentImpl,
- Fn,
- Other,
- }
-
- let node_kind = match node {
- Node::TraitItem(item) => match item.kind {
- TraitItemKind::Fn(..) => NodeKind::Fn,
- _ => NodeKind::Other,
- },
-
- Node::ImplItem(item) => match item.kind {
- ImplItemKind::Fn(..) => NodeKind::Fn,
- _ => NodeKind::Other,
- },
-
- Node::Item(item) => match item.kind {
- ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => NodeKind::TraitImpl,
- ItemKind::Impl(hir::Impl { of_trait: None, .. }) => NodeKind::InherentImpl,
- ItemKind::Fn(..) => NodeKind::Fn,
- _ => NodeKind::Other,
- },
-
- Node::ForeignItem(item) => match item.kind {
- ForeignItemKind::Fn(..) => NodeKind::Fn,
- _ => NodeKind::Other,
- },
-
- // FIXME: closures?
- _ => NodeKind::Other,
- };
-
- // FIXME(eddyb) isn't the unordered nature of this a hazard?
- let mut inputs = FxIndexSet::default();
-
- match node_kind {
- // In a trait impl, we assume that the header trait ref and all its
- // constituents are well-formed.
- NodeKind::TraitImpl => {
- let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl").subst_identity();
-
- // FIXME(chalk): this has problems because of late-bound regions
- //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
- inputs.extend(trait_ref.substs.iter());
- }
-
- // In an inherent impl, we assume that the receiver type and all its
- // constituents are well-formed.
- NodeKind::InherentImpl => {
- let self_ty = tcx.type_of(def_id).subst_identity();
- inputs.extend(self_ty.walk());
- }
-
- // In an fn, we assume that the arguments and all their constituents are
- // well-formed.
- NodeKind::Fn => {
- let fn_sig = tcx.fn_sig(def_id).subst_identity();
- let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig);
-
- inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
- }
-
- NodeKind::Other => (),
- }
- let input_clauses = inputs.into_iter().filter_map(|arg| {
- match arg.unpack() {
- GenericArgKind::Type(ty) => {
- let binder = Binder::dummy(PredicateKind::TypeWellFormedFromEnv(ty));
- Some(tcx.mk_predicate(binder))
- }
-
- // FIXME(eddyb) no WF conditions from lifetimes?
- GenericArgKind::Lifetime(_) => None,
-
- // FIXME(eddyb) support const generics in Chalk
- GenericArgKind::Const(_) => None,
- }
- });
-
- tcx.mk_predicates_from_iter(clauses.chain(input_clauses))
-}
-
fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
tcx.param_env(def_id).with_reveal_all_normalized(tcx)
}
@@ -508,7 +375,7 @@ fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<EarlyBinder<Ty<'
if self_ty_matches {
debug!("issue33140_self_ty - MATCHES!");
- Some(EarlyBinder(self_ty))
+ Some(EarlyBinder::bind(self_ty))
} else {
debug!("issue33140_self_ty - non-matching self type");
None
@@ -575,7 +442,7 @@ pub fn provide(providers: &mut Providers) {
param_env_reveal_all_normalized,
instance_def_size_estimate,
issue33140_self_ty,
- impl_defaultness,
+ defaultness,
unsizing_params_for_adt,
..*providers
};
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index f6b44bdf2..878a6b784 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -52,21 +52,32 @@ pub trait Interner: Sized {
type PolyFnSig: Clone + Debug + Hash + Ord;
type ListBinderExistentialPredicate: Clone + Debug + Hash + Ord;
type BinderListTy: Clone + Debug + Hash + Ord;
- type ListTy: Clone + Debug + Hash + Ord;
+ type ListTy: Clone + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>;
type AliasTy: Clone + Debug + Hash + Ord;
type ParamTy: Clone + Debug + Hash + Ord;
type BoundTy: Clone + Debug + Hash + Ord;
type PlaceholderType: Clone + Debug + Hash + Ord;
- type InferTy: Clone + Debug + Hash + Ord;
type ErrorGuaranteed: Clone + Debug + Hash + Ord;
type PredicateKind: Clone + Debug + Hash + PartialEq + Eq;
type AllocId: Clone + Debug + Hash + Ord;
+ type InferConst: Clone + Debug + Hash + Ord;
+ type AliasConst: Clone + Debug + Hash + Ord;
+ type PlaceholderConst: Clone + Debug + Hash + Ord;
+ type ParamConst: Clone + Debug + Hash + Ord;
+ type BoundConst: Clone + Debug + Hash + Ord;
+ type InferTy: Clone + Debug + Hash + Ord;
+ type ValueConst: Clone + Debug + Hash + Ord;
+ type ExprConst: Clone + Debug + Hash + Ord;
+
type EarlyBoundRegion: Clone + Debug + Hash + Ord;
type BoundRegion: Clone + Debug + Hash + Ord;
type FreeRegion: Clone + Debug + Hash + Ord;
type RegionVid: Clone + Debug + Hash + Ord;
type PlaceholderRegion: Clone + Debug + Hash + Ord;
+
+ fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Self::Mutability);
+ fn mutability_is_mut(mutbl: Self::Mutability) -> bool;
}
/// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
@@ -390,7 +401,19 @@ impl DebruijnIndex {
}
}
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub fn debug_bound_var<T: std::fmt::Write>(
+ fmt: &mut T,
+ debruijn: DebruijnIndex,
+ var: impl std::fmt::Debug,
+) -> Result<(), std::fmt::Error> {
+ if debruijn == INNERMOST {
+ write!(fmt, "^{:?}", var)
+ } else {
+ write!(fmt, "^{}_{:?}", debruijn.index(), var)
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum IntTy {
Isize,
@@ -448,7 +471,7 @@ impl IntTy {
}
}
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Debug)]
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum UintTy {
Usize,
@@ -506,7 +529,7 @@ impl UintTy {
}
}
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum FloatTy {
F32,
diff --git a/compiler/rustc_type_ir/src/structural_impls.rs b/compiler/rustc_type_ir/src/structural_impls.rs
index 45a2e9023..1e42175f6 100644
--- a/compiler/rustc_type_ir/src/structural_impls.rs
+++ b/compiler/rustc_type_ir/src/structural_impls.rs
@@ -4,11 +4,12 @@
use crate::fold::{FallibleTypeFolder, TypeFoldable};
use crate::visit::{TypeVisitable, TypeVisitor};
-use crate::Interner;
+use crate::{ConstKind, FloatTy, IntTy, Interner, UintTy};
use rustc_data_structures::functor::IdFunctor;
use rustc_data_structures::sync::Lrc;
use rustc_index::{Idx, IndexVec};
+use core::fmt;
use std::ops::ControlFlow;
///////////////////////////////////////////////////////////////////////////
@@ -163,3 +164,39 @@ impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
+
+impl fmt::Debug for IntTy {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.name_str())
+ }
+}
+
+impl fmt::Debug for UintTy {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.name_str())
+ }
+}
+
+impl fmt::Debug for FloatTy {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.name_str())
+ }
+}
+
+impl<I: Interner> fmt::Debug for ConstKind<I> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use ConstKind::*;
+ match self {
+ Param(param) => write!(f, "{param:?}"),
+ Infer(var) => write!(f, "{var:?}"),
+ Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var.clone()),
+ Placeholder(placeholder) => write!(f, "{placeholder:?}"),
+ Unevaluated(uv) => {
+ write!(f, "{uv:?}")
+ }
+ Value(valtree) => write!(f, "{valtree:?}"),
+ Error(_) => write!(f, "{{const error}}"),
+ Expr(expr) => write!(f, "{expr:?}"),
+ }
+ }
+}
diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs
index f7344bacc..b696f9b9b 100644
--- a/compiler/rustc_type_ir/src/sty.rs
+++ b/compiler/rustc_type_ir/src/sty.rs
@@ -36,9 +36,17 @@ pub enum DynKind {
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Encodable, Decodable, HashStable_Generic)]
pub enum AliasKind {
+ /// A projection `<Type as Trait>::AssocType`.
+ /// Can get normalized away if monomorphic enough.
Projection,
Inherent,
+ /// An opaque type (usually from `impl Trait` in type aliases or function return types)
+ /// Can only be normalized away in RevealAll mode
Opaque,
+ /// A type alias that actually checks its trait bounds.
+ /// Currently only used if the type alias references opaque types.
+ /// Can always be normalized away.
+ Weak,
}
/// Defines the kinds of types used by the type system.
@@ -294,7 +302,7 @@ impl<I: Interner> Clone for TyKind<I> {
Str => Str,
Array(t, c) => Array(t.clone(), c.clone()),
Slice(t) => Slice(t.clone()),
- RawPtr(t) => RawPtr(t.clone()),
+ RawPtr(p) => RawPtr(p.clone()),
Ref(r, t, m) => Ref(r.clone(), t.clone(), m.clone()),
FnDef(d, s) => FnDef(d.clone(), s.clone()),
FnPtr(s) => FnPtr(s.clone()),
@@ -499,33 +507,65 @@ impl<I: Interner> hash::Hash for TyKind<I> {
impl<I: Interner> fmt::Debug for TyKind<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Bool => f.write_str("Bool"),
- Char => f.write_str("Char"),
- Int(i) => f.debug_tuple_field1_finish("Int", i),
- Uint(u) => f.debug_tuple_field1_finish("Uint", u),
- Float(float) => f.debug_tuple_field1_finish("Float", float),
+ Bool => write!(f, "bool"),
+ Char => write!(f, "char"),
+ Int(i) => write!(f, "{i:?}"),
+ Uint(u) => write!(f, "{u:?}"),
+ Float(float) => write!(f, "{float:?}"),
Adt(d, s) => f.debug_tuple_field2_finish("Adt", d, s),
Foreign(d) => f.debug_tuple_field1_finish("Foreign", d),
- Str => f.write_str("Str"),
- Array(t, c) => f.debug_tuple_field2_finish("Array", t, c),
- Slice(t) => f.debug_tuple_field1_finish("Slice", t),
- RawPtr(t) => f.debug_tuple_field1_finish("RawPtr", t),
- Ref(r, t, m) => f.debug_tuple_field3_finish("Ref", r, t, m),
+ Str => write!(f, "str"),
+ Array(t, c) => write!(f, "[{t:?}; {c:?}]"),
+ Slice(t) => write!(f, "[{t:?}]"),
+ RawPtr(p) => {
+ let (ty, mutbl) = I::ty_and_mut_to_parts(p.clone());
+ match I::mutability_is_mut(mutbl) {
+ true => write!(f, "*mut "),
+ false => write!(f, "*const "),
+ }?;
+ write!(f, "{ty:?}")
+ }
+ Ref(r, t, m) => match I::mutability_is_mut(m.clone()) {
+ true => write!(f, "&{r:?} mut {t:?}"),
+ false => write!(f, "&{r:?} {t:?}"),
+ },
FnDef(d, s) => f.debug_tuple_field2_finish("FnDef", d, s),
- FnPtr(s) => f.debug_tuple_field1_finish("FnPtr", s),
- Dynamic(p, r, repr) => f.debug_tuple_field3_finish("Dynamic", p, r, repr),
+ FnPtr(s) => write!(f, "{s:?}"),
+ Dynamic(p, r, repr) => match repr {
+ DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"),
+ DynKind::DynStar => write!(f, "dyn* {p:?} + {r:?}"),
+ },
Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, s),
Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, s, m),
GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", g),
GeneratorWitnessMIR(d, s) => f.debug_tuple_field2_finish("GeneratorWitnessMIR", d, s),
- Never => f.write_str("Never"),
- Tuple(t) => f.debug_tuple_field1_finish("Tuple", t),
+ Never => write!(f, "!"),
+ Tuple(t) => {
+ let mut iter = t.clone().into_iter();
+
+ write!(f, "(")?;
+
+ match iter.next() {
+ None => return write!(f, ")"),
+ Some(ty) => write!(f, "{ty:?}")?,
+ };
+
+ match iter.next() {
+ None => return write!(f, ",)"),
+ Some(ty) => write!(f, "{ty:?})")?,
+ }
+
+ for ty in iter {
+ write!(f, ", {ty:?}")?;
+ }
+ write!(f, ")")
+ }
Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, a),
- Param(p) => f.debug_tuple_field1_finish("Param", p),
- Bound(d, b) => f.debug_tuple_field2_finish("Bound", d, b),
- Placeholder(p) => f.debug_tuple_field1_finish("Placeholder", p),
- Infer(t) => f.debug_tuple_field1_finish("Infer", t),
- TyKind::Error(e) => f.debug_tuple_field1_finish("Error", e),
+ Param(p) => write!(f, "{p:?}"),
+ Bound(d, b) => crate::debug_bound_var(f, *d, b),
+ Placeholder(p) => write!(f, "{p:?}"),
+ Infer(t) => write!(f, "{t:?}"),
+ TyKind::Error(_) => write!(f, "{{type error}}"),
}
}
}
@@ -836,6 +876,224 @@ where
}
}
+/// Represents a constant in Rust.
+// #[derive(derive_more::From)]
+pub enum ConstKind<I: Interner> {
+ /// A const generic parameter.
+ Param(I::ParamConst),
+
+ /// Infer the value of the const.
+ Infer(I::InferConst),
+
+ /// Bound const variable, used only when preparing a trait query.
+ Bound(DebruijnIndex, I::BoundConst),
+
+ /// A placeholder const - universally quantified higher-ranked const.
+ Placeholder(I::PlaceholderConst),
+
+ /// An unnormalized const item such as an anon const or assoc const or free const item.
+ /// Right now anything other than anon consts does not actually work properly but this
+ /// should
+ Unevaluated(I::AliasConst),
+
+ /// Used to hold computed value.
+ Value(I::ValueConst),
+
+ /// A placeholder for a const which could not be computed; this is
+ /// propagated to avoid useless error messages.
+ Error(I::ErrorGuaranteed),
+
+ /// Unevaluated non-const-item, used by `feature(generic_const_exprs)` to represent
+ /// const arguments such as `N + 1` or `foo(N)`
+ Expr(I::ExprConst),
+}
+
+const fn const_kind_discriminant<I: Interner>(value: &ConstKind<I>) -> usize {
+ match value {
+ ConstKind::Param(_) => 0,
+ ConstKind::Infer(_) => 1,
+ ConstKind::Bound(_, _) => 2,
+ ConstKind::Placeholder(_) => 3,
+ ConstKind::Unevaluated(_) => 4,
+ ConstKind::Value(_) => 5,
+ ConstKind::Error(_) => 6,
+ ConstKind::Expr(_) => 7,
+ }
+}
+
+impl<I: Interner> hash::Hash for ConstKind<I> {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) {
+ const_kind_discriminant(self).hash(state);
+ match self {
+ ConstKind::Param(p) => p.hash(state),
+ ConstKind::Infer(i) => i.hash(state),
+ ConstKind::Bound(d, b) => {
+ d.hash(state);
+ b.hash(state);
+ }
+ ConstKind::Placeholder(p) => p.hash(state),
+ ConstKind::Unevaluated(u) => u.hash(state),
+ ConstKind::Value(v) => v.hash(state),
+ ConstKind::Error(e) => e.hash(state),
+ ConstKind::Expr(e) => e.hash(state),
+ }
+ }
+}
+
+impl<CTX: HashStableContext, I: Interner> HashStable<CTX> for ConstKind<I>
+where
+ I::ParamConst: HashStable<CTX>,
+ I::InferConst: HashStable<CTX>,
+ I::BoundConst: HashStable<CTX>,
+ I::PlaceholderConst: HashStable<CTX>,
+ I::AliasConst: HashStable<CTX>,
+ I::ValueConst: HashStable<CTX>,
+ I::ErrorGuaranteed: HashStable<CTX>,
+ I::ExprConst: HashStable<CTX>,
+{
+ fn hash_stable(
+ &self,
+ hcx: &mut CTX,
+ hasher: &mut rustc_data_structures::stable_hasher::StableHasher,
+ ) {
+ const_kind_discriminant(self).hash_stable(hcx, hasher);
+ match self {
+ ConstKind::Param(p) => p.hash_stable(hcx, hasher),
+ ConstKind::Infer(i) => i.hash_stable(hcx, hasher),
+ ConstKind::Bound(d, b) => {
+ d.hash_stable(hcx, hasher);
+ b.hash_stable(hcx, hasher);
+ }
+ ConstKind::Placeholder(p) => p.hash_stable(hcx, hasher),
+ ConstKind::Unevaluated(u) => u.hash_stable(hcx, hasher),
+ ConstKind::Value(v) => v.hash_stable(hcx, hasher),
+ ConstKind::Error(e) => e.hash_stable(hcx, hasher),
+ ConstKind::Expr(e) => e.hash_stable(hcx, hasher),
+ }
+ }
+}
+
+impl<I: Interner, D: TyDecoder<I = I>> Decodable<D> for ConstKind<I>
+where
+ I::ParamConst: Decodable<D>,
+ I::InferConst: Decodable<D>,
+ I::BoundConst: Decodable<D>,
+ I::PlaceholderConst: Decodable<D>,
+ I::AliasConst: Decodable<D>,
+ I::ValueConst: Decodable<D>,
+ I::ErrorGuaranteed: Decodable<D>,
+ I::ExprConst: Decodable<D>,
+{
+ fn decode(d: &mut D) -> Self {
+ match Decoder::read_usize(d) {
+ 0 => ConstKind::Param(Decodable::decode(d)),
+ 1 => ConstKind::Infer(Decodable::decode(d)),
+ 2 => ConstKind::Bound(Decodable::decode(d), Decodable::decode(d)),
+ 3 => ConstKind::Placeholder(Decodable::decode(d)),
+ 4 => ConstKind::Unevaluated(Decodable::decode(d)),
+ 5 => ConstKind::Value(Decodable::decode(d)),
+ 6 => ConstKind::Error(Decodable::decode(d)),
+ 7 => ConstKind::Expr(Decodable::decode(d)),
+ _ => panic!(
+ "{}",
+ format!(
+ "invalid enum variant tag while decoding `{}`, expected 0..{}",
+ "ConstKind", 8,
+ )
+ ),
+ }
+ }
+}
+
+impl<I: Interner, E: TyEncoder<I = I>> Encodable<E> for ConstKind<I>
+where
+ I::ParamConst: Encodable<E>,
+ I::InferConst: Encodable<E>,
+ I::BoundConst: Encodable<E>,
+ I::PlaceholderConst: Encodable<E>,
+ I::AliasConst: Encodable<E>,
+ I::ValueConst: Encodable<E>,
+ I::ErrorGuaranteed: Encodable<E>,
+ I::ExprConst: Encodable<E>,
+{
+ fn encode(&self, e: &mut E) {
+ let disc = const_kind_discriminant(self);
+ match self {
+ ConstKind::Param(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
+ ConstKind::Infer(i) => e.emit_enum_variant(disc, |e| i.encode(e)),
+ ConstKind::Bound(d, b) => e.emit_enum_variant(disc, |e| {
+ d.encode(e);
+ b.encode(e);
+ }),
+ ConstKind::Placeholder(p) => e.emit_enum_variant(disc, |e| p.encode(e)),
+ ConstKind::Unevaluated(u) => e.emit_enum_variant(disc, |e| u.encode(e)),
+ ConstKind::Value(v) => e.emit_enum_variant(disc, |e| v.encode(e)),
+ ConstKind::Error(er) => e.emit_enum_variant(disc, |e| er.encode(e)),
+ ConstKind::Expr(ex) => e.emit_enum_variant(disc, |e| ex.encode(e)),
+ }
+ }
+}
+
+impl<I: Interner> PartialOrd for ConstKind<I> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl<I: Interner> Ord for ConstKind<I> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ const_kind_discriminant(self)
+ .cmp(&const_kind_discriminant(other))
+ .then_with(|| match (self, other) {
+ (ConstKind::Param(p1), ConstKind::Param(p2)) => p1.cmp(p2),
+ (ConstKind::Infer(i1), ConstKind::Infer(i2)) => i1.cmp(i2),
+ (ConstKind::Bound(d1, b1), ConstKind::Bound(d2, b2)) => d1.cmp(d2).then_with(|| b1.cmp(b2)),
+ (ConstKind::Placeholder(p1), ConstKind::Placeholder(p2)) => p1.cmp(p2),
+ (ConstKind::Unevaluated(u1), ConstKind::Unevaluated(u2)) => u1.cmp(u2),
+ (ConstKind::Value(v1), ConstKind::Value(v2)) => v1.cmp(v2),
+ (ConstKind::Error(e1), ConstKind::Error(e2)) => e1.cmp(e2),
+ (ConstKind::Expr(e1), ConstKind::Expr(e2)) => e1.cmp(e2),
+ _ => {
+ debug_assert!(false, "This branch must be unreachable, maybe the match is missing an arm? self = {self:?}, other = {other:?}");
+ Ordering::Equal
+ }
+ })
+ }
+}
+
+impl<I: Interner> PartialEq for ConstKind<I> {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Self::Param(l0), Self::Param(r0)) => l0 == r0,
+ (Self::Infer(l0), Self::Infer(r0)) => l0 == r0,
+ (Self::Bound(l0, l1), Self::Bound(r0, r1)) => l0 == r0 && l1 == r1,
+ (Self::Placeholder(l0), Self::Placeholder(r0)) => l0 == r0,
+ (Self::Unevaluated(l0), Self::Unevaluated(r0)) => l0 == r0,
+ (Self::Value(l0), Self::Value(r0)) => l0 == r0,
+ (Self::Error(l0), Self::Error(r0)) => l0 == r0,
+ (Self::Expr(l0), Self::Expr(r0)) => l0 == r0,
+ _ => false,
+ }
+ }
+}
+
+impl<I: Interner> Eq for ConstKind<I> {}
+
+impl<I: Interner> Clone for ConstKind<I> {
+ fn clone(&self) -> Self {
+ match self {
+ Self::Param(arg0) => Self::Param(arg0.clone()),
+ Self::Infer(arg0) => Self::Infer(arg0.clone()),
+ Self::Bound(arg0, arg1) => Self::Bound(arg0.clone(), arg1.clone()),
+ Self::Placeholder(arg0) => Self::Placeholder(arg0.clone()),
+ Self::Unevaluated(arg0) => Self::Unevaluated(arg0.clone()),
+ Self::Value(arg0) => Self::Value(arg0.clone()),
+ Self::Error(arg0) => Self::Error(arg0.clone()),
+ Self::Expr(arg0) => Self::Expr(arg0.clone()),
+ }
+ }
+}
+
/// Representation of regions. Note that the NLL checker uses a distinct
/// representation of regions. For this reason, it internally replaces all the
/// regions with inference variables -- the index of the variable is then used