Merge from rustc
This commit is contained in:
commit
87042866a3
2258 changed files with 11565 additions and 9075 deletions
|
|
@ -20,3 +20,6 @@ f97fddab91fbf290ea5b691fe355d6f915220b6e
|
|||
cc907f80b95c6ec530c5ee1b05b044a468f07eca
|
||||
# format let-chains
|
||||
b2d2184edea578109a48ec3d8decbee5948e8f35
|
||||
# test directives migration
|
||||
6e48b96692d63a79a14563f27fe5185f122434f8
|
||||
ec2cc761bc7067712ecc7734502f703fe3b024c8
|
||||
|
|
|
|||
44
Cargo.lock
44
Cargo.lock
|
|
@ -1224,19 +1224,6 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.2"
|
||||
|
|
@ -2058,17 +2045,6 @@ version = "2.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
|
|
@ -2364,7 +2340,7 @@ dependencies = [
|
|||
"clap",
|
||||
"clap_complete",
|
||||
"elasticlunr-rs",
|
||||
"env_logger 0.11.2",
|
||||
"env_logger",
|
||||
"handlebars",
|
||||
"log",
|
||||
"memchr",
|
||||
|
|
@ -2709,7 +2685,7 @@ dependencies = [
|
|||
"camino",
|
||||
"clap",
|
||||
"derive_builder",
|
||||
"env_logger 0.10.2",
|
||||
"env_logger",
|
||||
"fs_extra",
|
||||
"glob",
|
||||
"humansize",
|
||||
|
|
@ -2788,9 +2764,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "papergrid"
|
||||
version = "0.10.0"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8"
|
||||
checksum = "9ad43c07024ef767f9160710b3a6773976194758c7919b17e63b863db0bdf7fb"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"fnv",
|
||||
|
|
@ -3326,7 +3302,7 @@ name = "rustbook"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger 0.10.2",
|
||||
"env_logger",
|
||||
"mdbook",
|
||||
]
|
||||
|
||||
|
|
@ -5271,16 +5247,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.29.11"
|
||||
version = "0.30.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666"
|
||||
checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"winapi",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5315,9 +5291,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tabled"
|
||||
version = "0.13.0"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d38d39c754ae037a9bc3ca1580a985db7371cd14f1229172d1db9093feb6739"
|
||||
checksum = "4c998b0c8b921495196a48aabaf1901ff28be0760136e31604f7967b0792050e"
|
||||
dependencies = [
|
||||
"papergrid",
|
||||
"unicode-width",
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
} else {
|
||||
self.tcx.fn_arg_names(sig_id).len()
|
||||
};
|
||||
let inputs = self.arena.alloc_from_iter((0..args_count).into_iter().map(|arg| hir::Ty {
|
||||
let inputs = self.arena.alloc_from_iter((0..args_count).map(|arg| hir::Ty {
|
||||
hir_id: self.next_id(),
|
||||
kind: hir::TyKind::InferDelegation(sig_id, hir::InferDelegationKind::Input(arg)),
|
||||
span: self.lower_span(param_span),
|
||||
|
|
|
|||
|
|
@ -1636,7 +1636,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
|
||||
old_def_id
|
||||
} else {
|
||||
self.dcx().span_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
self.dcx()
|
||||
.span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,13 @@ borrowck_returned_lifetime_wrong =
|
|||
borrowck_returned_ref_escaped =
|
||||
returns a reference to a captured variable which escapes the closure body
|
||||
|
||||
borrowck_simd_shuffle_last_const = last argument of `simd_shuffle` is required to be a `const` item
|
||||
borrowck_simd_intrinsic_arg_const =
|
||||
{$arg ->
|
||||
[1] 1st
|
||||
[2] 2nd
|
||||
[3] 3rd
|
||||
*[other] {$arg}th
|
||||
} argument of `{$intrinsic}` is required to be a `const` item
|
||||
|
||||
borrowck_suggest_create_freash_reborrow =
|
||||
consider reborrowing the `Pin` instead of moving it
|
||||
|
|
|
|||
|
|
@ -1559,7 +1559,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
|||
// A bare path doesn't need a `let` assignment, it's already a simple
|
||||
// binding access.
|
||||
// As a new binding wasn't added, we don't need to modify the advancing call.
|
||||
sugg.push((loop_span.with_hi(pat_span.lo()), format!("while let Some(")));
|
||||
sugg.push((loop_span.with_hi(pat_span.lo()), "while let Some(".to_string()));
|
||||
sugg.push((
|
||||
pat_span.shrink_to_hi().with_hi(head.span.lo()),
|
||||
") = ".to_string(),
|
||||
|
|
|
|||
|
|
@ -134,14 +134,13 @@ impl OutlivesSuggestionBuilder {
|
|||
|
||||
for (r, bound) in unified.into_iter() {
|
||||
if !unified_already.contains(fr) {
|
||||
suggested.push(SuggestedConstraint::Equal(fr_name.clone(), bound));
|
||||
suggested.push(SuggestedConstraint::Equal(fr_name, bound));
|
||||
unified_already.insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
if !other.is_empty() {
|
||||
let other =
|
||||
other.iter().map(|(_, rname)| rname.clone()).collect::<SmallVec<_>>();
|
||||
let other = other.iter().map(|(_, rname)| *rname).collect::<SmallVec<_>>();
|
||||
suggested.push(SuggestedConstraint::Outlives(fr_name, other))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
.iter()
|
||||
.rfind(|param| param.def_id.to_def_id() == defid)
|
||||
.is_some() {
|
||||
suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static")));
|
||||
suggestions.push((bounded_span.shrink_to_hi(), " + 'static".to_string()));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -153,12 +153,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
if let Some(prev) = result.get_mut(&opaque_type_key.def_id) {
|
||||
if prev.ty != ty {
|
||||
let guar = ty.error_reported().err().unwrap_or_else(|| {
|
||||
prev.report_mismatch(
|
||||
&OpaqueHiddenType { ty, span: concrete_type.span },
|
||||
opaque_type_key.def_id,
|
||||
infcx.tcx,
|
||||
)
|
||||
.emit()
|
||||
let (Ok(e) | Err(e)) = prev
|
||||
.build_mismatch_error(
|
||||
&OpaqueHiddenType { ty, span: concrete_type.span },
|
||||
opaque_type_key.def_id,
|
||||
infcx.tcx,
|
||||
)
|
||||
.map(|d| d.emit());
|
||||
e
|
||||
});
|
||||
prev.ty = Ty::new_error(infcx.tcx, guar);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -454,8 +454,10 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(borrowck_simd_shuffle_last_const)]
|
||||
pub(crate) struct SimdShuffleLastConst {
|
||||
#[diag(borrowck_simd_intrinsic_arg_const)]
|
||||
pub(crate) struct SimdIntrinsicArgConst {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub arg: usize,
|
||||
pub intrinsic: String,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
|||
use rustc_mir_dataflow::move_paths::MoveData;
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
||||
use crate::session_diagnostics::{MoveUnsized, SimdShuffleLastConst};
|
||||
use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
|
||||
use crate::{
|
||||
borrow_set::BorrowSet,
|
||||
constraints::{OutlivesConstraint, OutlivesConstraintSet},
|
||||
|
|
@ -1664,9 +1664,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
let func_ty = func.ty(body, self.infcx.tcx);
|
||||
if let ty::FnDef(def_id, _) = *func_ty.kind() {
|
||||
if let Some(sym::simd_shuffle) = self.tcx().intrinsic(def_id) {
|
||||
if !matches!(args[2], Spanned { node: Operand::Constant(_), .. }) {
|
||||
self.tcx().dcx().emit_err(SimdShuffleLastConst { span: term.source_info.span });
|
||||
// Some of the SIMD intrinsics are special: they need a particular argument to be a constant.
|
||||
// (Eventually this should use const-generics, but those are not up for the task yet:
|
||||
// https://github.com/rust-lang/rust/issues/85229.)
|
||||
if let Some(name @ (sym::simd_shuffle | sym::simd_insert | sym::simd_extract)) =
|
||||
self.tcx().intrinsic(def_id)
|
||||
{
|
||||
let idx = match name {
|
||||
sym::simd_shuffle => 2,
|
||||
_ => 1,
|
||||
};
|
||||
if !matches!(args[idx], Spanned { node: Operand::Constant(_), .. }) {
|
||||
self.tcx().dcx().emit_err(SimdIntrinsicArgConst {
|
||||
span: term.source_info.span,
|
||||
arg: idx + 1,
|
||||
intrinsic: name.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,11 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
|
|||
// `handle_opaque_type` cannot handle subtyping, so to support subtyping
|
||||
// we instead eagerly generalize here. This is a bit of a mess but will go
|
||||
// away once we're using the new solver.
|
||||
let mut enable_subtyping = |ty, ty_is_expected| {
|
||||
//
|
||||
// Given `opaque rel B`, we create a new infer var `ty_vid` constrain it
|
||||
// by using `ty_vid rel B` and then finally and end by equating `ty_vid` to
|
||||
// the opaque.
|
||||
let mut enable_subtyping = |ty, opaque_is_expected| {
|
||||
let ty_vid = infcx.next_ty_var_id_in_universe(
|
||||
TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
|
|
@ -132,7 +136,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
|
|||
ty::UniverseIndex::ROOT,
|
||||
);
|
||||
|
||||
let variance = if ty_is_expected {
|
||||
let variance = if opaque_is_expected {
|
||||
self.ambient_variance
|
||||
} else {
|
||||
self.ambient_variance.xform(ty::Contravariant)
|
||||
|
|
@ -140,7 +144,7 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
|
|||
|
||||
self.type_checker.infcx.instantiate_ty_var(
|
||||
self,
|
||||
ty_is_expected,
|
||||
opaque_is_expected,
|
||||
ty_vid,
|
||||
variance,
|
||||
ty,
|
||||
|
|
@ -149,8 +153,8 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
|
|||
};
|
||||
|
||||
let (a, b) = match (a.kind(), b.kind()) {
|
||||
(&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, false)?),
|
||||
(_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, true)?, b),
|
||||
(&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, true)?),
|
||||
(_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, false)?, b),
|
||||
_ => unreachable!(
|
||||
"expected at least one opaque type in `relate_opaques`, got {a} and {b}."
|
||||
),
|
||||
|
|
|
|||
|
|
@ -743,7 +743,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_nanless => {
|
||||
sym::simd_reduce_min => {
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
if !v.layout().ty.is_simd() {
|
||||
|
|
@ -762,7 +762,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
|
|||
});
|
||||
}
|
||||
|
||||
sym::simd_reduce_max | sym::simd_reduce_max_nanless => {
|
||||
sym::simd_reduce_max => {
|
||||
intrinsic_args!(fx, args => (v); intrinsic);
|
||||
|
||||
if !v.layout().ty.is_simd() {
|
||||
|
|
|
|||
|
|
@ -1752,7 +1752,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
self.vector_reduce(src, |a, b, context| context.new_binary_op(None, op, a.get_type(), a, b))
|
||||
}
|
||||
|
||||
pub fn vector_reduce_fadd_fast(&mut self, _acc: RValue<'gcc>, _src: RValue<'gcc>) -> RValue<'gcc> {
|
||||
pub fn vector_reduce_fadd_reassoc(&mut self, _acc: RValue<'gcc>, _src: RValue<'gcc>) -> RValue<'gcc> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
|
@ -1772,7 +1772,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn vector_reduce_fmul_fast(&mut self, _acc: RValue<'gcc>, _src: RValue<'gcc>) -> RValue<'gcc> {
|
||||
pub fn vector_reduce_fmul_reassoc(&mut self, _acc: RValue<'gcc>, _src: RValue<'gcc>) -> RValue<'gcc> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -989,14 +989,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
|
|||
|
||||
arith_red!(
|
||||
simd_reduce_add_unordered: BinaryOp::Plus,
|
||||
vector_reduce_fadd_fast,
|
||||
vector_reduce_fadd_reassoc,
|
||||
false,
|
||||
add,
|
||||
0.0 // TODO: Use this argument.
|
||||
);
|
||||
arith_red!(
|
||||
simd_reduce_mul_unordered: BinaryOp::Mult,
|
||||
vector_reduce_fmul_fast,
|
||||
vector_reduce_fmul_reassoc,
|
||||
false,
|
||||
mul,
|
||||
1.0
|
||||
|
|
@ -1041,9 +1041,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
|
|||
|
||||
minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
|
||||
minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
|
||||
// TODO(sadlerap): revisit these intrinsics to generate more optimal reductions
|
||||
minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin);
|
||||
minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax);
|
||||
|
||||
macro_rules! bitwise_red {
|
||||
($name:ident : $op:expr, $boolean:expr) => {
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ fn get_llvm_object_symbols(
|
|||
llvm::LLVMRustGetSymbols(
|
||||
buf.as_ptr(),
|
||||
buf.len(),
|
||||
&mut *state as *mut &mut _ as *mut c_void,
|
||||
std::ptr::addr_of_mut!(*state) as *mut c_void,
|
||||
callback,
|
||||
error_callback,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1367,17 +1367,17 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
pub fn vector_reduce_fmul(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
|
||||
unsafe { llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src) }
|
||||
}
|
||||
pub fn vector_reduce_fadd_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
|
||||
pub fn vector_reduce_fadd_reassoc(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
|
||||
unsafe {
|
||||
let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src);
|
||||
llvm::LLVMRustSetAlgebraicMath(instr);
|
||||
llvm::LLVMRustSetAllowReassoc(instr);
|
||||
instr
|
||||
}
|
||||
}
|
||||
pub fn vector_reduce_fmul_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
|
||||
pub fn vector_reduce_fmul_reassoc(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
|
||||
unsafe {
|
||||
let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src);
|
||||
llvm::LLVMRustSetAlgebraicMath(instr);
|
||||
llvm::LLVMRustSetAllowReassoc(instr);
|
||||
instr
|
||||
}
|
||||
}
|
||||
|
|
@ -1406,22 +1406,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
|||
llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false)
|
||||
}
|
||||
}
|
||||
pub fn vector_reduce_fmin_fast(&mut self, src: &'ll Value) -> &'ll Value {
|
||||
unsafe {
|
||||
let instr =
|
||||
llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true);
|
||||
llvm::LLVMRustSetFastMath(instr);
|
||||
instr
|
||||
}
|
||||
}
|
||||
pub fn vector_reduce_fmax_fast(&mut self, src: &'ll Value) -> &'ll Value {
|
||||
unsafe {
|
||||
let instr =
|
||||
llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true);
|
||||
llvm::LLVMRustSetFastMath(instr);
|
||||
instr
|
||||
}
|
||||
}
|
||||
pub fn vector_reduce_min(&mut self, src: &'ll Value, is_signed: bool) -> &'ll Value {
|
||||
unsafe { llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,8 +103,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_
|
|||
fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
|
||||
let diag: DiagnosticBuilder<'_, G> = self.0.into_diagnostic(dcx, level);
|
||||
let (message, _) = diag.messages.first().expect("`LlvmError` with no message");
|
||||
let message = dcx.eagerly_translate_to_string(message.clone(), diag.args());
|
||||
|
||||
let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter());
|
||||
DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config)
|
||||
.with_arg("error", message)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1079,7 +1079,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
.map(|(arg_idx, val)| {
|
||||
let idx = val.unwrap_leaf().try_to_i32().unwrap();
|
||||
if idx >= i32::try_from(total_len).unwrap() {
|
||||
bx.sess().dcx().emit_err(InvalidMonomorphization::ShuffleIndexOutOfBounds {
|
||||
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx: arg_idx as u64,
|
||||
|
|
@ -1138,24 +1138,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
let val = bx.const_get_elt(vector, i as u64);
|
||||
match bx.const_to_opt_u128(val, true) {
|
||||
None => {
|
||||
bx.sess().dcx().emit_err(
|
||||
InvalidMonomorphization::ShuffleIndexNotConstant {
|
||||
span,
|
||||
name,
|
||||
arg_idx,
|
||||
},
|
||||
);
|
||||
None
|
||||
bug!("typeck should have already ensured that these are const")
|
||||
}
|
||||
Some(idx) if idx >= total_len => {
|
||||
bx.sess().dcx().emit_err(
|
||||
InvalidMonomorphization::ShuffleIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx,
|
||||
total_len,
|
||||
},
|
||||
);
|
||||
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx,
|
||||
total_len,
|
||||
});
|
||||
None
|
||||
}
|
||||
Some(idx) => Some(bx.const_i32(idx as i32)),
|
||||
|
|
@ -1184,10 +1175,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
out_ty: arg_tys[2]
|
||||
}
|
||||
);
|
||||
let idx = bx
|
||||
.const_to_opt_u128(args[1].immediate(), false)
|
||||
.expect("typeck should have ensure that this is a const");
|
||||
if idx >= in_len.into() {
|
||||
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx: 1,
|
||||
total_len: in_len.into(),
|
||||
});
|
||||
return Ok(bx.const_null(llret_ty));
|
||||
}
|
||||
return Ok(bx.insert_element(
|
||||
args[0].immediate(),
|
||||
args[2].immediate(),
|
||||
args[1].immediate(),
|
||||
bx.const_i32(idx as i32),
|
||||
));
|
||||
}
|
||||
if name == sym::simd_extract {
|
||||
|
|
@ -1195,7 +1198,19 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
ret_ty == in_elem,
|
||||
InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty }
|
||||
);
|
||||
return Ok(bx.extract_element(args[0].immediate(), args[1].immediate()));
|
||||
let idx = bx
|
||||
.const_to_opt_u128(args[1].immediate(), false)
|
||||
.expect("typeck should have ensure that this is a const");
|
||||
if idx >= in_len.into() {
|
||||
bx.sess().dcx().emit_err(InvalidMonomorphization::SimdIndexOutOfBounds {
|
||||
span,
|
||||
name,
|
||||
arg_idx: 1,
|
||||
total_len: in_len.into(),
|
||||
});
|
||||
return Ok(bx.const_null(llret_ty));
|
||||
}
|
||||
return Ok(bx.extract_element(args[0].immediate(), bx.const_i32(idx as i32)));
|
||||
}
|
||||
|
||||
if name == sym::simd_select {
|
||||
|
|
@ -1880,14 +1895,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
|
||||
arith_red!(
|
||||
simd_reduce_add_unordered: vector_reduce_add,
|
||||
vector_reduce_fadd_algebraic,
|
||||
vector_reduce_fadd_reassoc,
|
||||
false,
|
||||
add,
|
||||
0.0
|
||||
);
|
||||
arith_red!(
|
||||
simd_reduce_mul_unordered: vector_reduce_mul,
|
||||
vector_reduce_fmul_algebraic,
|
||||
vector_reduce_fmul_reassoc,
|
||||
false,
|
||||
mul,
|
||||
1.0
|
||||
|
|
@ -1920,9 +1935,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
|
|||
minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin);
|
||||
minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax);
|
||||
|
||||
minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin_fast);
|
||||
minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax_fast);
|
||||
|
||||
macro_rules! bitwise_red {
|
||||
($name:ident : $red:ident, $boolean:expr) => {
|
||||
if name == sym::$name {
|
||||
|
|
|
|||
|
|
@ -169,7 +169,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
fn print_pass_timings(&self) {
|
||||
unsafe {
|
||||
let mut size = 0;
|
||||
let cstr = llvm::LLVMRustPrintPassTimings(&mut size as *mut usize);
|
||||
let cstr = llvm::LLVMRustPrintPassTimings(std::ptr::addr_of_mut!(size));
|
||||
if cstr.is_null() {
|
||||
println!("failed to get pass timings");
|
||||
} else {
|
||||
|
|
@ -182,7 +182,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
|
|||
fn print_statistics(&self) {
|
||||
unsafe {
|
||||
let mut size = 0;
|
||||
let cstr = llvm::LLVMRustPrintStatistics(&mut size as *mut usize);
|
||||
let cstr = llvm::LLVMRustPrintStatistics(std::ptr::addr_of_mut!(size));
|
||||
if cstr.is_null() {
|
||||
println!("failed to get pass stats");
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1619,6 +1619,7 @@ extern "C" {
|
|||
|
||||
pub fn LLVMRustSetFastMath(Instr: &Value);
|
||||
pub fn LLVMRustSetAlgebraicMath(Instr: &Value);
|
||||
pub fn LLVMRustSetAllowReassoc(Instr: &Value);
|
||||
|
||||
// Miscellaneous instructions
|
||||
pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess
|
|||
&tm,
|
||||
cpu_cstring.as_ptr(),
|
||||
callback,
|
||||
&mut out as *mut &mut dyn PrintBackendInfo as *mut c_void,
|
||||
std::ptr::addr_of_mut!(out) as *mut c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,14 +106,12 @@ codegen_ssa_invalid_monomorphization_return_type = invalid monomorphization of `
|
|||
|
||||
codegen_ssa_invalid_monomorphization_second_argument_length = invalid monomorphization of `{$name}` intrinsic: expected second argument with length {$in_len} (same as input type `{$in_ty}`), found `{$arg_ty}` with length {$out_len}
|
||||
|
||||
codegen_ssa_invalid_monomorphization_shuffle_index_not_constant = invalid monomorphization of `{$name}` intrinsic: shuffle index #{$arg_idx} is not a constant
|
||||
|
||||
codegen_ssa_invalid_monomorphization_shuffle_index_out_of_bounds = invalid monomorphization of `{$name}` intrinsic: shuffle index #{$arg_idx} is out of bounds (limit {$total_len})
|
||||
|
||||
codegen_ssa_invalid_monomorphization_simd_argument = invalid monomorphization of `{$name}` intrinsic: expected SIMD argument type, found non-SIMD `{$ty}`
|
||||
|
||||
codegen_ssa_invalid_monomorphization_simd_first = invalid monomorphization of `{$name}` intrinsic: expected SIMD first type, found non-SIMD `{$ty}`
|
||||
|
||||
codegen_ssa_invalid_monomorphization_simd_index_out_of_bounds = invalid monomorphization of `{$name}` intrinsic: SIMD index #{$arg_idx} is out of bounds (limit {$total_len})
|
||||
|
||||
codegen_ssa_invalid_monomorphization_simd_input = invalid monomorphization of `{$name}` intrinsic: expected SIMD input type, found non-SIMD `{$ty}`
|
||||
|
||||
codegen_ssa_invalid_monomorphization_simd_return = invalid monomorphization of `{$name}` intrinsic: expected SIMD return type, found non-SIMD `{$ty}`
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ pub fn create_compressed_metadata_file_for_xcoff(
|
|||
/// that contains a custom section of the name `section_name` with contents
|
||||
/// `data`.
|
||||
///
|
||||
/// NB: the `object` crate does not yet have support for writing the the wasm
|
||||
/// NB: the `object` crate does not yet have support for writing the wasm
|
||||
/// object file format. The format is simple enough that for now an extra crate
|
||||
/// from crates.io (such as `wasm-encoder`). The file format is:
|
||||
///
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ use rustc_data_structures::sync::Lrc;
|
|||
use rustc_errors::emitter::Emitter;
|
||||
use rustc_errors::translation::Translate;
|
||||
use rustc_errors::{
|
||||
DiagCtxt, DiagnosticArgName, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrCode,
|
||||
FatalError, FluentBundle, Level, Style,
|
||||
DiagCtxt, DiagnosticArgMap, DiagnosticBuilder, DiagnosticMessage, ErrCode, FatalError,
|
||||
FluentBundle, Level, MultiSpan, Style,
|
||||
};
|
||||
use rustc_fs_util::link_or_copy;
|
||||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
|
||||
|
|
@ -999,11 +999,29 @@ pub(crate) enum Message<B: WriteBackendMethods> {
|
|||
/// process another codegen unit.
|
||||
pub struct CguMessage;
|
||||
|
||||
// A cut-down version of `rustc_errors::Diagnostic` that impls `Send`, which
|
||||
// can be used to send diagnostics from codegen threads to the main thread.
|
||||
// It's missing the following fields from `rustc_errors::Diagnostic`.
|
||||
// - `span`: it doesn't impl `Send`.
|
||||
// - `suggestions`: it doesn't impl `Send`, and isn't used for codegen
|
||||
// diagnostics.
|
||||
// - `sort_span`: it doesn't impl `Send`.
|
||||
// - `is_lint`: lints aren't relevant during codegen.
|
||||
// - `emitted_at`: not used for codegen diagnostics.
|
||||
struct Diagnostic {
|
||||
msgs: Vec<(DiagnosticMessage, Style)>,
|
||||
args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>,
|
||||
level: Level,
|
||||
messages: Vec<(DiagnosticMessage, Style)>,
|
||||
code: Option<ErrCode>,
|
||||
lvl: Level,
|
||||
children: Vec<Subdiagnostic>,
|
||||
args: DiagnosticArgMap,
|
||||
}
|
||||
|
||||
// A cut-down version of `rustc_errors::SubDiagnostic` that impls `Send`. It's
|
||||
// missing the following fields from `rustc_errors::SubDiagnostic`.
|
||||
// - `span`: it doesn't impl `Send`.
|
||||
pub struct Subdiagnostic {
|
||||
level: Level,
|
||||
messages: Vec<(DiagnosticMessage, Style)>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
|
|
@ -1766,7 +1784,6 @@ fn spawn_work<'a, B: ExtraBackendMethods>(
|
|||
enum SharedEmitterMessage {
|
||||
Diagnostic(Diagnostic),
|
||||
InlineAsmError(u32, String, Level, Option<(String, Vec<InnerSpan>)>),
|
||||
AbortIfErrors,
|
||||
Fatal(String),
|
||||
}
|
||||
|
||||
|
|
@ -1812,24 +1829,29 @@ impl Translate for SharedEmitter {
|
|||
}
|
||||
|
||||
impl Emitter for SharedEmitter {
|
||||
fn emit_diagnostic(&mut self, diag: rustc_errors::Diagnostic) {
|
||||
let args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue> =
|
||||
diag.args().map(|(name, arg)| (name.clone(), arg.clone())).collect();
|
||||
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||
msgs: diag.messages.clone(),
|
||||
args: args.clone(),
|
||||
code: diag.code,
|
||||
lvl: diag.level(),
|
||||
})));
|
||||
for child in &diag.children {
|
||||
drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||
msgs: child.messages.clone(),
|
||||
args: args.clone(),
|
||||
code: None,
|
||||
lvl: child.level,
|
||||
})));
|
||||
}
|
||||
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors));
|
||||
fn emit_diagnostic(&mut self, mut diag: rustc_errors::Diagnostic) {
|
||||
// Check that we aren't missing anything interesting when converting to
|
||||
// the cut-down local `Diagnostic`.
|
||||
assert_eq!(diag.span, MultiSpan::new());
|
||||
assert_eq!(diag.suggestions, Ok(vec![]));
|
||||
assert_eq!(diag.sort_span, rustc_span::DUMMY_SP);
|
||||
assert_eq!(diag.is_lint, None);
|
||||
// No sensible check for `diag.emitted_at`.
|
||||
|
||||
let args = mem::replace(&mut diag.args, DiagnosticArgMap::default());
|
||||
drop(
|
||||
self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
|
||||
level: diag.level(),
|
||||
messages: diag.messages,
|
||||
code: diag.code,
|
||||
children: diag
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|child| Subdiagnostic { level: child.level, messages: child.messages })
|
||||
.collect(),
|
||||
args,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
fn source_map(&self) -> Option<&Lrc<SourceMap>> {
|
||||
|
|
@ -1854,11 +1876,24 @@ impl SharedEmitterMain {
|
|||
|
||||
match message {
|
||||
Ok(SharedEmitterMessage::Diagnostic(diag)) => {
|
||||
// The diagnostic has been received on the main thread.
|
||||
// Convert it back to a full `Diagnostic` and emit.
|
||||
let dcx = sess.dcx();
|
||||
let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs);
|
||||
let mut d =
|
||||
rustc_errors::Diagnostic::new_with_messages(diag.level, diag.messages);
|
||||
d.code = diag.code; // may be `None`, that's ok
|
||||
d.replace_args(diag.args);
|
||||
d.children = diag
|
||||
.children
|
||||
.into_iter()
|
||||
.map(|sub| rustc_errors::SubDiagnostic {
|
||||
level: sub.level,
|
||||
messages: sub.messages,
|
||||
span: MultiSpan::new(),
|
||||
})
|
||||
.collect();
|
||||
d.args = diag.args;
|
||||
dcx.emit_diagnostic(d);
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
|
||||
assert!(matches!(level, Level::Error | Level::Warning | Level::Note));
|
||||
|
|
@ -1891,9 +1926,6 @@ impl SharedEmitterMain {
|
|||
|
||||
err.emit();
|
||||
}
|
||||
Ok(SharedEmitterMessage::AbortIfErrors) => {
|
||||
sess.dcx().abort_if_errors();
|
||||
}
|
||||
Ok(SharedEmitterMessage::Fatal(msg)) => {
|
||||
sess.dcx().fatal(msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -797,16 +797,8 @@ pub enum InvalidMonomorphization<'tcx> {
|
|||
out_ty: Ty<'tcx>,
|
||||
},
|
||||
|
||||
#[diag(codegen_ssa_invalid_monomorphization_shuffle_index_not_constant, code = E0511)]
|
||||
ShuffleIndexNotConstant {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
name: Symbol,
|
||||
arg_idx: u64,
|
||||
},
|
||||
|
||||
#[diag(codegen_ssa_invalid_monomorphization_shuffle_index_out_of_bounds, code = E0511)]
|
||||
ShuffleIndexOutOfBounds {
|
||||
#[diag(codegen_ssa_invalid_monomorphization_simd_index_out_of_bounds, code = E0511)]
|
||||
SimdIndexOutOfBounds {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
name: Symbol,
|
||||
|
|
|
|||
|
|
@ -319,7 +319,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
targets: &SwitchTargets,
|
||||
) {
|
||||
let discr = self.codegen_operand(bx, discr);
|
||||
let discr_value = discr.immediate();
|
||||
let switch_ty = discr.layout.ty;
|
||||
// If our discriminant is a constant we can branch directly
|
||||
if let Some(const_discr) = bx.const_to_opt_u128(discr_value, false) {
|
||||
let target = targets.target_for_value(const_discr);
|
||||
bx.br(helper.llbb_with_cleanup(self, target));
|
||||
return;
|
||||
};
|
||||
|
||||
let mut target_iter = targets.iter();
|
||||
if target_iter.len() == 1 {
|
||||
// If there are two targets (one conditional, one fallback), emit `br` instead of
|
||||
|
|
@ -330,14 +338,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
if switch_ty == bx.tcx().types.bool {
|
||||
// Don't generate trivial icmps when switching on bool.
|
||||
match test_value {
|
||||
0 => bx.cond_br(discr.immediate(), llfalse, lltrue),
|
||||
1 => bx.cond_br(discr.immediate(), lltrue, llfalse),
|
||||
0 => bx.cond_br(discr_value, llfalse, lltrue),
|
||||
1 => bx.cond_br(discr_value, lltrue, llfalse),
|
||||
_ => bug!(),
|
||||
}
|
||||
} else {
|
||||
let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty));
|
||||
let llval = bx.const_uint_big(switch_llty, test_value);
|
||||
let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
|
||||
let cmp = bx.icmp(IntPredicate::IntEQ, discr_value, llval);
|
||||
bx.cond_br(cmp, lltrue, llfalse);
|
||||
}
|
||||
} else if self.cx.sess().opts.optimize == OptLevel::No
|
||||
|
|
@ -362,11 +370,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let ll2 = helper.llbb_with_cleanup(self, target2);
|
||||
let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty));
|
||||
let llval = bx.const_uint_big(switch_llty, test_value1);
|
||||
let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
|
||||
let cmp = bx.icmp(IntPredicate::IntEQ, discr_value, llval);
|
||||
bx.cond_br(cmp, ll1, ll2);
|
||||
} else {
|
||||
bx.switch(
|
||||
discr.immediate(),
|
||||
discr_value,
|
||||
helper.llbb_with_cleanup(self, targets.otherwise()),
|
||||
target_iter.map(|(value, target)| (value, helper.llbb_with_cleanup(self, target))),
|
||||
);
|
||||
|
|
@ -864,8 +872,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
.map(|(i, arg)| {
|
||||
// The indices passed to simd_shuffle in the
|
||||
// third argument must be constant. This is
|
||||
// checked by const-qualification, which also
|
||||
// promotes any complex rvalues to constants.
|
||||
// checked by the type-checker.
|
||||
if i == 2 && intrinsic == sym::simd_shuffle {
|
||||
if let mir::Operand::Constant(constant) = &arg.node {
|
||||
let (llval, ty) = self.simd_shuffle_indices(bx, constant);
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ pub trait ReportErrorExt {
|
|||
let mut diag = dcx.struct_allow(DiagnosticMessage::Str(String::new().into()));
|
||||
let message = self.diagnostic_message();
|
||||
self.add_args(&mut diag);
|
||||
let s = dcx.eagerly_translate_to_string(message, diag.args());
|
||||
let s = dcx.eagerly_translate_to_string(message, diag.args.iter());
|
||||
diag.cancel();
|
||||
s
|
||||
})
|
||||
|
|
@ -864,7 +864,7 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
|
|||
let dummy_level = Level::Bug;
|
||||
let dummy_diag: DiagnosticBuilder<'_, ()> =
|
||||
e.into_diagnostic().into_diagnostic(diag.dcx, dummy_level);
|
||||
for (name, val) in dummy_diag.args() {
|
||||
for (name, val) in dummy_diag.args.iter() {
|
||||
diag.arg(name.clone(), val.clone());
|
||||
}
|
||||
dummy_diag.cancel();
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> St
|
|||
let mut diag = dcx.struct_allow("");
|
||||
let msg = e.diagnostic_message();
|
||||
e.add_args(&mut diag);
|
||||
let s = dcx.eagerly_translate_to_string(msg, diag.args());
|
||||
let s = dcx.eagerly_translate_to_string(msg, diag.args.iter());
|
||||
diag.cancel();
|
||||
s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ pub fn intern_const_alloc_recursive<
|
|||
alloc.1.mutability = base_mutability;
|
||||
alloc.1.provenance().ptrs().iter().map(|&(_, prov)| prov).collect()
|
||||
} else {
|
||||
intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().map(|prov| prov).collect()
|
||||
intern_shallow(ecx, base_alloc_id, base_mutability).unwrap().collect()
|
||||
};
|
||||
// We need to distinguish "has just been interned" from "was already in `tcx`",
|
||||
// so we track this in a separate set.
|
||||
|
|
@ -277,7 +277,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
|||
// We are not doing recursive interning, so we don't currently support provenance.
|
||||
// (If this assertion ever triggers, we should just implement a
|
||||
// proper recursive interning loop -- or just call `intern_const_alloc_recursive`.
|
||||
if !self.tcx.try_get_global_alloc(prov.alloc_id()).is_some() {
|
||||
if self.tcx.try_get_global_alloc(prov.alloc_id()).is_none() {
|
||||
panic!("`intern_with_temp_alloc` with nested allocations");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -379,10 +379,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
let (input, input_len) = self.operand_to_simd(&args[0])?;
|
||||
let (dest, dest_len) = self.place_to_simd(dest)?;
|
||||
assert_eq!(input_len, dest_len, "Return vector length must match input length");
|
||||
assert!(
|
||||
index < dest_len,
|
||||
"Index `{index}` must be in bounds of vector with length {dest_len}"
|
||||
);
|
||||
// Bounds are not checked by typeck so we have to do it ourselves.
|
||||
if index >= input_len {
|
||||
throw_ub_format!(
|
||||
"`simd_insert` index {index} is out-of-bounds of vector with length {input_len}"
|
||||
);
|
||||
}
|
||||
|
||||
for i in 0..dest_len {
|
||||
let place = self.project_index(&dest, i)?;
|
||||
|
|
@ -397,10 +399,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
sym::simd_extract => {
|
||||
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
|
||||
let (input, input_len) = self.operand_to_simd(&args[0])?;
|
||||
assert!(
|
||||
index < input_len,
|
||||
"index `{index}` must be in bounds of vector with length {input_len}"
|
||||
);
|
||||
// Bounds are not checked by typeck so we have to do it ourselves.
|
||||
if index >= input_len {
|
||||
throw_ub_format!(
|
||||
"`simd_extract` index {index} is out-of-bounds of vector with length {input_len}"
|
||||
);
|
||||
}
|
||||
self.copy_op(&self.project_index(&input, index)?, dest)?;
|
||||
}
|
||||
sym::likely | sym::unlikely | sym::black_box => {
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ impl<T> RwLock<T> {
|
|||
#[inline(always)]
|
||||
pub fn leak(&self) -> &T {
|
||||
let guard = self.read();
|
||||
let ret = unsafe { &*(&*guard as *const T) };
|
||||
let ret = unsafe { &*std::ptr::addr_of!(*guard) };
|
||||
std::mem::forget(guard);
|
||||
ret
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl Translate for AnnotateSnippetEmitter {
|
|||
impl Emitter for AnnotateSnippetEmitter {
|
||||
/// The entry point for the diagnostics generation
|
||||
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||
let fluent_args = to_fluent_args(diag.args());
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
||||
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ pub enum DiagnosticArgValue {
|
|||
StrListSepByAnd(Vec<Cow<'static, str>>),
|
||||
}
|
||||
|
||||
pub type DiagnosticArgMap = FxIndexMap<DiagnosticArgName, DiagnosticArgValue>;
|
||||
|
||||
/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
|
||||
/// (or "proof") token that the emission happened.
|
||||
pub trait EmissionGuarantee: Sized {
|
||||
|
|
@ -275,7 +277,7 @@ pub struct Diagnostic {
|
|||
pub span: MultiSpan,
|
||||
pub children: Vec<SubDiagnostic>,
|
||||
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
|
||||
args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>,
|
||||
pub args: DiagnosticArgMap,
|
||||
|
||||
/// This is not used for highlighting or rendering any error message. Rather, it can be used
|
||||
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
|
||||
|
|
@ -403,14 +405,6 @@ impl Diagnostic {
|
|||
self.args.insert(name.into(), arg.into_diagnostic_arg());
|
||||
}
|
||||
|
||||
pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
|
||||
self.args.iter()
|
||||
}
|
||||
|
||||
pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
|
||||
self.args = args;
|
||||
}
|
||||
|
||||
/// Fields used for Hash, and PartialEq trait.
|
||||
fn keys(
|
||||
&self,
|
||||
|
|
@ -431,7 +425,7 @@ impl Diagnostic {
|
|||
&self.span,
|
||||
&self.children,
|
||||
&self.suggestions,
|
||||
self.args().collect(),
|
||||
self.args.iter().collect(),
|
||||
// omit self.sort_span
|
||||
&self.is_lint,
|
||||
// omit self.emitted_at
|
||||
|
|
@ -1165,7 +1159,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
|
|||
subdiagnostic: impl AddToDiagnostic,
|
||||
) -> &mut Self {
|
||||
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
|
||||
let args = diag.args();
|
||||
let args = diag.args.iter();
|
||||
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
|
||||
dcx.eagerly_translate(msg, args)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -519,7 +519,7 @@ impl Emitter for HumanEmitter {
|
|||
}
|
||||
|
||||
fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
|
||||
let fluent_args = to_fluent_args(diag.args());
|
||||
let fluent_args = to_fluent_args(diag.args.iter());
|
||||
|
||||
let mut suggestions = diag.suggestions.unwrap_or(vec![]);
|
||||
self.primary_span_formatted(&mut diag.span, &mut suggestions, &fluent_args);
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ struct UnusedExterns<'a, 'b, 'c> {
|
|||
|
||||
impl Diagnostic {
|
||||
fn from_errors_diagnostic(diag: crate::Diagnostic, je: &JsonEmitter) -> Diagnostic {
|
||||
let args = to_fluent_args(diag.args());
|
||||
let args = to_fluent_args(diag.args.iter());
|
||||
let sugg = diag.suggestions.iter().flatten().map(|sugg| {
|
||||
let translated_message =
|
||||
je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap();
|
||||
|
|
|
|||
|
|
@ -37,9 +37,10 @@ extern crate self as rustc_errors;
|
|||
|
||||
pub use codes::*;
|
||||
pub use diagnostic::{
|
||||
AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName,
|
||||
DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString, EmissionGuarantee, FatalAbort,
|
||||
IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic, SubdiagnosticMessageOp,
|
||||
AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgMap,
|
||||
DiagnosticArgName, DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString,
|
||||
EmissionGuarantee, FatalAbort, IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic,
|
||||
SubdiagnosticMessageOp,
|
||||
};
|
||||
pub use diagnostic_impls::{
|
||||
DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
|
||||
|
|
@ -843,7 +844,7 @@ impl DiagCtxt {
|
|||
.emitted_diagnostic_codes
|
||||
.iter()
|
||||
.filter_map(|&code| {
|
||||
if registry.try_find_description(code).is_ok().clone() {
|
||||
if registry.try_find_description(code).is_ok() {
|
||||
Some(code.to_string())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -1496,9 +1497,8 @@ impl DiagCtxtInner {
|
|||
diag: &Diagnostic,
|
||||
msg: impl Into<SubdiagnosticMessage>,
|
||||
) -> SubdiagnosticMessage {
|
||||
let args = diag.args();
|
||||
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
|
||||
self.eagerly_translate(msg, args)
|
||||
self.eagerly_translate(msg, diag.args.iter())
|
||||
}
|
||||
|
||||
fn flush_delayed(&mut self) {
|
||||
|
|
|
|||
|
|
@ -555,23 +555,14 @@ fn count_repetitions<'a>(
|
|||
) -> PResult<'a, usize> {
|
||||
// Recursively count the number of matches in `matched` at given depth
|
||||
// (or at the top-level of `matched` if no depth is given).
|
||||
fn count<'a>(
|
||||
cx: &ExtCtxt<'a>,
|
||||
depth_curr: usize,
|
||||
depth_max: usize,
|
||||
matched: &NamedMatch,
|
||||
sp: &DelimSpan,
|
||||
) -> PResult<'a, usize> {
|
||||
fn count<'a>(depth_curr: usize, depth_max: usize, matched: &NamedMatch) -> PResult<'a, usize> {
|
||||
match matched {
|
||||
MatchedTokenTree(_) | MatchedNonterminal(_) => Ok(1),
|
||||
MatchedSeq(named_matches) => {
|
||||
if depth_curr == depth_max {
|
||||
Ok(named_matches.len())
|
||||
} else {
|
||||
named_matches
|
||||
.iter()
|
||||
.map(|elem| count(cx, depth_curr + 1, depth_max, elem, sp))
|
||||
.sum()
|
||||
named_matches.iter().map(|elem| count(depth_curr + 1, depth_max, elem)).sum()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -612,7 +603,7 @@ fn count_repetitions<'a>(
|
|||
return Err(cx.dcx().create_err(CountRepetitionMisplaced { span: sp.entire() }));
|
||||
}
|
||||
|
||||
count(cx, depth_user, depth_max, matched, sp)
|
||||
count(depth_user, depth_max, matched)
|
||||
}
|
||||
|
||||
/// Returns a `NamedMatch` item declared on the LHS given an arbitrary [Ident]
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
|
|||
|
||||
fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
|
||||
let message = rustc_errors::DiagnosticMessage::from(diagnostic.message);
|
||||
let mut diag: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed> =
|
||||
let mut diag: DiagnosticBuilder<'_, ()> =
|
||||
DiagnosticBuilder::new(&self.sess().dcx, diagnostic.level.to_internal(), message);
|
||||
diag.span(MultiSpan::from_spans(diagnostic.spans));
|
||||
for child in diagnostic.children {
|
||||
|
|
|
|||
|
|
@ -792,6 +792,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
rustc_intrinsic, Normal, template!(Word), ErrorFollowing,
|
||||
"the `#[rustc_intrinsic]` attribute is used to declare intrinsics with function bodies",
|
||||
),
|
||||
rustc_attr!(
|
||||
rustc_no_mir_inline, Normal, template!(Word), WarnFollowing,
|
||||
"#[rustc_no_mir_inline] prevents the MIR inliner from inlining a function while not affecting codegen"
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes, Testing:
|
||||
|
|
|
|||
|
|
@ -518,7 +518,7 @@ declare_features! (
|
|||
(unstable, marker_trait_attr, "1.30.0", Some(29864)),
|
||||
/// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are
|
||||
/// unambiguously sound.
|
||||
(incomplete, min_exhaustive_patterns, "1.77.0", Some(119612)),
|
||||
(unstable, min_exhaustive_patterns, "1.77.0", Some(119612)),
|
||||
/// A minimal, sound subset of specialization intended to be used by the
|
||||
/// standard library until the soundness issues with specialization
|
||||
/// are fixed.
|
||||
|
|
|
|||
|
|
@ -3004,6 +3004,11 @@ impl<'hir> Item<'hir> {
|
|||
matches!(self.kind, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..))
|
||||
}
|
||||
|
||||
/// Check if this is an [`ItemKind::Struct`] or [`ItemKind::Union`].
|
||||
pub fn is_struct_or_union(&self) -> bool {
|
||||
matches!(self.kind, ItemKind::Struct(..) | ItemKind::Union(..))
|
||||
}
|
||||
|
||||
expect_methods_self_kind! {
|
||||
expect_extern_crate, Option<Symbol>, ItemKind::ExternCrate(s), *s;
|
||||
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@ fn sanity_check_found_hidden_type<'tcx>(
|
|||
} else {
|
||||
let span = tcx.def_span(key.def_id);
|
||||
let other = ty::OpaqueHiddenType { ty: hidden_ty, span };
|
||||
Err(ty.report_mismatch(&other, key.def_id, tcx).emit())
|
||||
Err(ty.build_mismatch_error(&other, key.def_id, tcx)?.emit())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -154,10 +154,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
|||
trait_m_sig.inputs_and_output,
|
||||
));
|
||||
if !ocx.select_all_or_error().is_empty() {
|
||||
// This code path is not reached in any tests, but may be reachable. If
|
||||
// this is triggered, it should be converted to `delayed_bug` and the
|
||||
// triggering case turned into a test.
|
||||
tcx.dcx().bug("encountered errors when checking RPITIT refinement (selection)");
|
||||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
|
||||
return;
|
||||
}
|
||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||
param_env,
|
||||
|
|
@ -165,10 +163,8 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
|
|||
);
|
||||
let errors = infcx.resolve_regions(&outlives_env);
|
||||
if !errors.is_empty() {
|
||||
// This code path is not reached in any tests, but may be reachable. If
|
||||
// this is triggered, it should be converted to `delayed_bug` and the
|
||||
// triggering case turned into a test.
|
||||
tcx.dcx().bug("encountered errors when checking RPITIT refinement (regions)");
|
||||
tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
|
||||
return;
|
||||
}
|
||||
// Resolve any lifetime variables that may have been introduced during normalization.
|
||||
let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
|
||||
|
|
|
|||
|
|
@ -606,9 +606,7 @@ pub fn check_platform_intrinsic_type(
|
|||
| sym::simd_reduce_or
|
||||
| sym::simd_reduce_xor
|
||||
| sym::simd_reduce_min
|
||||
| sym::simd_reduce_max
|
||||
| sym::simd_reduce_min_nanless
|
||||
| sym::simd_reduce_max_nanless => (2, 0, vec![param(0)], param(1)),
|
||||
| sym::simd_reduce_max => (2, 0, vec![param(0)], param(1)),
|
||||
sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
|
||||
sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
|
||||
_ => {
|
||||
|
|
|
|||
|
|
@ -1025,9 +1025,17 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
|
||||
let is_anonymous = item.ident.name == kw::Empty;
|
||||
let repr = if is_anonymous {
|
||||
tcx.adt_def(tcx.local_parent(def_id)).repr()
|
||||
let parent = tcx.local_parent(def_id);
|
||||
if let Node::Item(item) = tcx.hir_node_by_def_id(parent)
|
||||
&& item.is_struct_or_union()
|
||||
{
|
||||
tcx.adt_def(parent).repr()
|
||||
} else {
|
||||
tcx.dcx().span_delayed_bug(item.span, "anonymous field inside non struct/union");
|
||||
ty::ReprOptions::default()
|
||||
}
|
||||
} else {
|
||||
tcx.repr_options_of_def(def_id.to_def_id())
|
||||
tcx.repr_options_of_def(def_id)
|
||||
};
|
||||
let (kind, variants) = match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
|||
|
||||
if is_host_effect {
|
||||
if let Some(idx) = host_effect_index {
|
||||
tcx.dcx().span_bug(
|
||||
tcx.dcx().span_delayed_bug(
|
||||
param.span,
|
||||
format!("parent also has host effect param? index: {idx}, def: {def_id:?}"),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -58,10 +58,10 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
|
|||
// 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();
|
||||
if concrete_type.ty != tcx.erase_regions(hidden.ty) {
|
||||
if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) {
|
||||
d.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -134,10 +134,10 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
|
|||
// 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();
|
||||
if concrete_type.ty != tcx.erase_regions(hidden.ty) {
|
||||
if let Ok(d) = hidden.build_mismatch_error(&concrete_type, def_id, tcx) {
|
||||
d.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -287,8 +287,10 @@ impl TaitConstraintLocator<'_> {
|
|||
if let Some(&concrete_type) = borrowck_results.concrete_opaque_types.get(&self.def_id) {
|
||||
debug!(?concrete_type, "found constraint");
|
||||
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();
|
||||
if concrete_type.ty != prev.ty {
|
||||
let (Ok(guar) | Err(guar)) = prev
|
||||
.build_mismatch_error(&concrete_type, self.def_id, self.tcx)
|
||||
.map(|d| d.emit());
|
||||
prev.ty = Ty::new_error(self.tcx, guar);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -361,11 +363,13 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
|
|||
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,
|
||||
);
|
||||
if concrete_type.ty != prev.ty {
|
||||
if let Ok(d) = prev.build_mismatch_error(&concrete_type, def_id, tcx) {
|
||||
d.stash(
|
||||
tcx.def_span(opaque_type_key.def_id),
|
||||
StashKey::OpaqueHiddenTypeMismatch,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hir_opaque_ty = Some(concrete_type);
|
||||
|
|
@ -436,9 +440,12 @@ impl RpitConstraintChecker<'_> {
|
|||
|
||||
debug!(?concrete_type, "found constraint");
|
||||
|
||||
if concrete_type.ty != self.found.ty && !(concrete_type, self.found).references_error()
|
||||
{
|
||||
self.found.report_mismatch(&concrete_type, self.def_id, self.tcx).emit();
|
||||
if concrete_type.ty != self.found.ty {
|
||||
if let Ok(d) =
|
||||
self.found.build_mismatch_error(&concrete_type, self.def_id, self.tcx)
|
||||
{
|
||||
d.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,12 +401,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
{
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == sp {
|
||||
return self.get_fn_decl(hir_id).and_then(|(_, fn_decl, _)| {
|
||||
return self.get_fn_decl(hir_id).map(|(_, fn_decl, _)| {
|
||||
let (ty, span) = match fn_decl.output {
|
||||
hir::FnRetTy::DefaultReturn(span) => ("()".to_string(), span),
|
||||
hir::FnRetTy::Return(ty) => (ty_to_string(ty), ty.span),
|
||||
};
|
||||
Some((span, format!("expected `{ty}` because of this return type")))
|
||||
(span, format!("expected `{ty}` because of this return type"))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,8 +77,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// coercions from ! to `expected`.
|
||||
if ty.is_never() {
|
||||
if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
|
||||
self.dcx()
|
||||
.span_bug(expr.span, "expression with never type wound up being adjusted");
|
||||
let reported = self.dcx().span_delayed_bug(
|
||||
expr.span,
|
||||
"expression with never type wound up being adjusted",
|
||||
);
|
||||
return Ty::new_error(self.tcx(), reported);
|
||||
}
|
||||
|
||||
let adj_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
|
|
|
|||
|
|
@ -846,7 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let item_name = item_segment.ident;
|
||||
let result = self
|
||||
.resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
|
||||
.and_then(|r| {
|
||||
.map(|r| {
|
||||
// lint bare trait if the method is found in the trait
|
||||
if span.edition().at_least_rust_2021()
|
||||
&& let Some(diag) =
|
||||
|
|
@ -854,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
{
|
||||
diag.emit();
|
||||
}
|
||||
Ok(r)
|
||||
r
|
||||
})
|
||||
.or_else(|error| {
|
||||
let guar = self
|
||||
|
|
@ -1522,10 +1522,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if self.next_trait_solver()
|
||||
&& let ty::Alias(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
// We need to use a separate variable here as otherwise the temporary for
|
||||
// `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting
|
||||
// in a reentrant borrow, causing an ICE.
|
||||
let result = self
|
||||
.at(&self.misc(sp), self.param_env)
|
||||
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut())
|
||||
{
|
||||
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut());
|
||||
match result {
|
||||
Ok(normalized_ty) => normalized_ty,
|
||||
Err(errors) => {
|
||||
let guar = self.err_ctxt().report_fulfillment_errors(errors);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use crate::coercion::CoerceMany;
|
||||
use crate::errors::SuggestPtrNullMut;
|
||||
use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx};
|
||||
use crate::fn_ctxt::infer::FnCall;
|
||||
use crate::gather_locals::Declaration;
|
||||
use crate::method::probe::IsSuggestion;
|
||||
use crate::method::probe::Mode::MethodCall;
|
||||
use crate::method::probe::ProbeScope::TraitsInScope;
|
||||
use crate::method::MethodCallee;
|
||||
use crate::TupleArgumentsFlag::*;
|
||||
use crate::{errors, Expectation::*};
|
||||
|
|
@ -451,7 +455,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
call_expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> ErrorGuaranteed {
|
||||
// Next, let's construct the error
|
||||
let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
|
||||
let (error_span, call_ident, full_call_span, call_name, is_method) = match &call_expr.kind {
|
||||
hir::ExprKind::Call(
|
||||
hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
|
||||
_,
|
||||
|
|
@ -463,12 +467,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
CtorOf::Struct => "struct",
|
||||
CtorOf::Variant => "enum variant",
|
||||
};
|
||||
(call_span, *span, name, false)
|
||||
(call_span, None, *span, name, false)
|
||||
} else {
|
||||
(call_span, *span, "function", false)
|
||||
(call_span, None, *span, "function", false)
|
||||
}
|
||||
}
|
||||
hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, "function", false),
|
||||
hir::ExprKind::Call(hir::Expr { span, .. }, _) => {
|
||||
(call_span, None, *span, "function", false)
|
||||
}
|
||||
hir::ExprKind::MethodCall(path_segment, _, _, span) => {
|
||||
let ident_span = path_segment.ident.span;
|
||||
let ident_span = if let Some(args) = path_segment.args {
|
||||
|
|
@ -476,7 +482,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else {
|
||||
ident_span
|
||||
};
|
||||
(*span, ident_span, "method", true)
|
||||
(*span, Some(path_segment.ident), ident_span, "method", true)
|
||||
}
|
||||
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
|
||||
};
|
||||
|
|
@ -530,6 +536,103 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let callee_ty = callee_expr
|
||||
.and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
|
||||
|
||||
// Obtain another method on `Self` that have similar name.
|
||||
let similar_assoc = |call_name: Ident| -> Option<(ty::AssocItem, ty::FnSig<'_>)> {
|
||||
if let Some(callee_ty) = callee_ty
|
||||
&& let Ok(Some(assoc)) = self.probe_op(
|
||||
call_name.span,
|
||||
MethodCall,
|
||||
Some(call_name),
|
||||
None,
|
||||
IsSuggestion(true),
|
||||
callee_ty.peel_refs(),
|
||||
callee_expr.unwrap().hir_id,
|
||||
TraitsInScope,
|
||||
|mut ctxt| ctxt.probe_for_similar_candidate(),
|
||||
)
|
||||
&& let ty::AssocKind::Fn = assoc.kind
|
||||
&& assoc.fn_has_self_parameter
|
||||
{
|
||||
let args = self.infcx.fresh_args_for_item(call_name.span, assoc.def_id);
|
||||
let fn_sig = tcx.fn_sig(assoc.def_id).instantiate(tcx, args);
|
||||
|
||||
self.instantiate_binder_with_fresh_vars(call_name.span, FnCall, fn_sig);
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let suggest_confusable = |err: &mut DiagnosticBuilder<'_>| {
|
||||
let Some(call_name) = call_ident else {
|
||||
return;
|
||||
};
|
||||
let Some(callee_ty) = callee_ty else {
|
||||
return;
|
||||
};
|
||||
let input_types: Vec<Ty<'_>> = provided_arg_tys.iter().map(|(ty, _)| *ty).collect();
|
||||
// Check for other methods in the following order
|
||||
// - methods marked as `rustc_confusables` with the provided arguments
|
||||
// - methods with the same argument type/count and short levenshtein distance
|
||||
// - methods marked as `rustc_confusables` (done)
|
||||
// - methods with short levenshtein distance
|
||||
|
||||
// Look for commonly confusable method names considering arguments.
|
||||
if let Some(_name) = self.confusable_method_name(
|
||||
err,
|
||||
callee_ty.peel_refs(),
|
||||
call_name,
|
||||
Some(input_types.clone()),
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Look for method names with short levenshtein distance, considering arguments.
|
||||
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
|
||||
&& fn_sig.inputs()[1..]
|
||||
.iter()
|
||||
.zip(input_types.iter())
|
||||
.all(|(expected, found)| self.can_coerce(*expected, *found))
|
||||
&& fn_sig.inputs()[1..].len() == input_types.len()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
call_name.span,
|
||||
format!("you might have meant to use `{}`", assoc.name),
|
||||
assoc.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Look for commonly confusable method names disregarding arguments.
|
||||
if let Some(_name) =
|
||||
self.confusable_method_name(err, callee_ty.peel_refs(), call_name, None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Look for similarly named methods with levenshtein distance with the right
|
||||
// number of arguments.
|
||||
if let Some((assoc, fn_sig)) = similar_assoc(call_name)
|
||||
&& fn_sig.inputs()[1..].len() == input_types.len()
|
||||
{
|
||||
err.span_note(
|
||||
tcx.def_span(assoc.def_id),
|
||||
format!(
|
||||
"there's is a method with similar name `{}`, but the arguments don't match",
|
||||
assoc.name,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Fallthrough: look for similarly named methods with levenshtein distance.
|
||||
if let Some((assoc, _)) = similar_assoc(call_name) {
|
||||
err.span_note(
|
||||
tcx.def_span(assoc.def_id),
|
||||
format!(
|
||||
"there's is a method with similar name `{}`, but their argument count \
|
||||
doesn't match",
|
||||
assoc.name,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
|
||||
// and treats error types differently
|
||||
// This will allow us to "probe" for other argument orders that would likely have been correct
|
||||
|
|
@ -694,6 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(mismatch_idx),
|
||||
is_method,
|
||||
);
|
||||
suggest_confusable(&mut err);
|
||||
return err.emit();
|
||||
}
|
||||
}
|
||||
|
|
@ -718,7 +822,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
if cfg!(debug_assertions) {
|
||||
span_bug!(error_span, "expected errors from argument matrix");
|
||||
} else {
|
||||
return tcx.dcx().emit_err(errors::ArgMismatchIndeterminate { span: error_span });
|
||||
let mut err =
|
||||
tcx.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span });
|
||||
suggest_confusable(&mut err);
|
||||
return err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -733,7 +840,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let trace =
|
||||
mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
|
||||
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
|
||||
reported = Some(self.err_ctxt().report_and_explain_type_error(trace, *e).emit());
|
||||
let mut err = self.err_ctxt().report_and_explain_type_error(trace, *e);
|
||||
suggest_confusable(&mut err);
|
||||
reported = Some(err.emit());
|
||||
return false;
|
||||
}
|
||||
true
|
||||
|
|
@ -802,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(expected_idx.as_usize()),
|
||||
is_method,
|
||||
);
|
||||
suggest_confusable(&mut err);
|
||||
return err.emit();
|
||||
}
|
||||
|
||||
|
|
@ -829,6 +939,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.with_code(err_code.to_owned())
|
||||
};
|
||||
|
||||
suggest_confusable(&mut err);
|
||||
// As we encounter issues, keep track of what we want to provide for the suggestion
|
||||
let mut labels = vec![];
|
||||
// If there is a single error, we give a specific suggestion; otherwise, we change to
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir_analysis::astconv::AstConv;
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::error_reporting::sub_relations::SubRelations;
|
||||
use rustc_infer::infer::error_reporting::TypeErrCtxt;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
|
|
@ -155,8 +156,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
///
|
||||
/// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
|
||||
pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
|
||||
let mut sub_relations = SubRelations::default();
|
||||
sub_relations.add_constraints(
|
||||
self,
|
||||
self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
|
||||
);
|
||||
TypeErrCtxt {
|
||||
infcx: &self.infcx,
|
||||
sub_relations: RefCell::new(sub_relations),
|
||||
typeck_results: Some(self.typeck_results.borrow()),
|
||||
fallback_has_occurred: self.fallback_has_occurred.get(),
|
||||
normalize_fn_sig: Box::new(|fn_sig| {
|
||||
|
|
|
|||
|
|
@ -1061,20 +1061,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
let scope = self
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(id)
|
||||
.filter(|(_, node)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
|
||||
| Node::Item(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::ImplItem(_)
|
||||
)
|
||||
})
|
||||
.next();
|
||||
let scope = self.tcx.hir().parent_iter(id).find(|(_, node)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
|
||||
| Node::Item(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::ImplItem(_)
|
||||
)
|
||||
});
|
||||
let in_closure =
|
||||
matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
|
||||
|
||||
|
|
|
|||
|
|
@ -582,7 +582,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
|||
match ty.kind() {
|
||||
ty::Tuple(args) => Ok(args.len()),
|
||||
_ => {
|
||||
self.tcx().dcx().span_bug(span, "tuple pattern not applied to a tuple");
|
||||
self.tcx().dcx().span_delayed_bug(span, "tuple pattern not applied to a tuple");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ pub use self::PickKind::*;
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct IsSuggestion(pub bool);
|
||||
|
||||
struct ProbeContext<'a, 'tcx> {
|
||||
pub(crate) struct ProbeContext<'a, 'tcx> {
|
||||
fcx: &'a FnCtxt<'a, 'tcx>,
|
||||
span: Span,
|
||||
mode: Mode,
|
||||
|
|
@ -355,7 +355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn probe_op<OP, R>(
|
||||
pub(crate) fn probe_op<OP, R>(
|
||||
&'a self,
|
||||
span: Span,
|
||||
mode: Mode,
|
||||
|
|
@ -1750,7 +1750,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
|
||||
/// candidate method where the method name may have been misspelled. Similarly to other
|
||||
/// edit distance based suggestions, we provide at most one such suggestion.
|
||||
fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
|
||||
pub(crate) fn probe_for_similar_candidate(
|
||||
&mut self,
|
||||
) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
|
||||
debug!("probing for method names similar to {:?}", self.method_name);
|
||||
|
||||
self.probe(|_| {
|
||||
|
|
@ -1766,6 +1768,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
);
|
||||
pcx.allow_similar_names = true;
|
||||
pcx.assemble_inherent_candidates();
|
||||
pcx.assemble_extension_candidates_for_all_traits();
|
||||
|
||||
let method_names = pcx.candidate_method_names(|_| true);
|
||||
pcx.allow_similar_names = false;
|
||||
|
|
@ -1775,6 +1778,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
pcx.reset();
|
||||
pcx.method_name = Some(method_name);
|
||||
pcx.assemble_inherent_candidates();
|
||||
pcx.assemble_extension_candidates_for_all_traits();
|
||||
pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item)
|
||||
})
|
||||
.collect();
|
||||
|
|
@ -1942,7 +1946,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id);
|
||||
let attrs = self.fcx.tcx.hir().attrs(hir_id);
|
||||
for attr in attrs {
|
||||
let sym::doc = attr.name_or_empty() else {
|
||||
if sym::doc == attr.name_or_empty() {
|
||||
} else if sym::rustc_confusables == attr.name_or_empty() {
|
||||
let Some(confusables) = attr.meta_item_list() else {
|
||||
continue;
|
||||
};
|
||||
// #[rustc_confusables("foo", "bar"))]
|
||||
for n in confusables {
|
||||
if let Some(lit) = n.lit()
|
||||
&& name.as_str() == lit.symbol.as_str()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let Some(values) = attr.meta_item_list() else {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use rustc_hir::PatKind::Binding;
|
|||
use rustc_hir::PathSegment;
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
use rustc_infer::infer::{
|
||||
self,
|
||||
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
|
||||
RegionVariableOrigin,
|
||||
};
|
||||
|
|
@ -274,7 +275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.span_if_local(def_id)
|
||||
.unwrap_or_else(|| self.tcx.def_span(def_id));
|
||||
err.span_label(sp, format!("private {kind} defined here"));
|
||||
self.suggest_valid_traits(&mut err, out_of_scope_traits, true);
|
||||
self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
|
||||
err.emit();
|
||||
}
|
||||
|
||||
|
|
@ -369,9 +370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
if let Some(file) = file {
|
||||
err.note(format!("the full type name has been written to '{}'", file.display()));
|
||||
err.note(format!(
|
||||
"consider using `--verbose` to print the full type name to the console"
|
||||
));
|
||||
err.note("consider using `--verbose` to print the full type name to the console");
|
||||
}
|
||||
|
||||
err
|
||||
|
|
@ -496,9 +495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
if let Some(file) = ty_file {
|
||||
err.note(format!("the full type name has been written to '{}'", file.display(),));
|
||||
err.note(format!(
|
||||
"consider using `--verbose` to print the full type name to the console"
|
||||
));
|
||||
err.note("consider using `--verbose` to print the full type name to the console");
|
||||
}
|
||||
if rcvr_ty.references_error() {
|
||||
err.downgrade_to_delayed_bug();
|
||||
|
|
@ -1209,32 +1206,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err.note(format!(
|
||||
"the {item_kind} was found for\n{type_candidates}{additional_types}"
|
||||
));
|
||||
} else {
|
||||
'outer: for inherent_impl_did in
|
||||
self.tcx.inherent_impls(adt.did()).into_iter().flatten()
|
||||
{
|
||||
for inherent_method in
|
||||
self.tcx.associated_items(inherent_impl_did).in_definition_order()
|
||||
{
|
||||
if let Some(attr) = self
|
||||
.tcx
|
||||
.get_attr(inherent_method.def_id, sym::rustc_confusables)
|
||||
&& let Some(candidates) = parse_confusables(attr)
|
||||
&& candidates.contains(&item_name.name)
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
item_name.span,
|
||||
format!(
|
||||
"you might have meant to use `{}`",
|
||||
inherent_method.name
|
||||
),
|
||||
inherent_method.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1260,6 +1231,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
label_span_not_found(&mut err);
|
||||
}
|
||||
|
||||
let confusable_suggested = self.confusable_method_name(
|
||||
&mut err,
|
||||
rcvr_ty,
|
||||
item_name,
|
||||
args.map(|args| {
|
||||
args.iter()
|
||||
.map(|expr| {
|
||||
self.node_ty_opt(expr.hir_id).unwrap_or_else(|| {
|
||||
self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: expr.span,
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}),
|
||||
);
|
||||
|
||||
// Don't suggest (for example) `expr.field.clone()` if `expr.clone()`
|
||||
// can't be called due to `typeof(expr): Clone` not holding.
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
|
|
@ -1361,31 +1350,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
} else if let Some(similar_candidate) = similar_candidate {
|
||||
// Don't emit a suggestion if we found an actual method
|
||||
// that had unsatisfied trait bounds
|
||||
if unsatisfied_predicates.is_empty() {
|
||||
let def_kind = similar_candidate.kind.as_def_kind();
|
||||
// Methods are defined within the context of a struct and their first parameter is always self,
|
||||
// which represents the instance of the struct the method is being called on
|
||||
// Associated functions don’t take self as a parameter and
|
||||
// they are not methods because they don’t have an instance of the struct to work with.
|
||||
if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"there is a method with a similar name",
|
||||
similar_candidate.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
format!(
|
||||
"there is {} {} with a similar name",
|
||||
self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id),
|
||||
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id)
|
||||
),
|
||||
similar_candidate.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
if unsatisfied_predicates.is_empty()
|
||||
// ...or if we already suggested that name because of `rustc_confusable` annotation.
|
||||
&& Some(similar_candidate.name) != confusable_suggested
|
||||
{
|
||||
self.find_likely_intended_associated_item(
|
||||
&mut err,
|
||||
similar_candidate,
|
||||
span,
|
||||
args,
|
||||
mode,
|
||||
);
|
||||
}
|
||||
}
|
||||
// If an appropriate error source is not found, check method chain for possible candiates
|
||||
|
|
@ -1437,6 +1412,146 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(err)
|
||||
}
|
||||
|
||||
fn find_likely_intended_associated_item(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
similar_candidate: ty::AssocItem,
|
||||
span: Span,
|
||||
args: Option<&'tcx [hir::Expr<'tcx>]>,
|
||||
mode: Mode,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let def_kind = similar_candidate.kind.as_def_kind();
|
||||
let an = self.tcx.def_kind_descr_article(def_kind, similar_candidate.def_id);
|
||||
let msg = format!(
|
||||
"there is {an} {} `{}` with a similar name",
|
||||
self.tcx.def_kind_descr(def_kind, similar_candidate.def_id),
|
||||
similar_candidate.name,
|
||||
);
|
||||
// Methods are defined within the context of a struct and their first parameter
|
||||
// is always `self`, which represents the instance of the struct the method is
|
||||
// being called on Associated functions don’t take self as a parameter and they are
|
||||
// not methods because they don’t have an instance of the struct to work with.
|
||||
if def_kind == DefKind::AssocFn {
|
||||
let ty_args = self.infcx.fresh_args_for_item(span, similar_candidate.def_id);
|
||||
let fn_sig = tcx.fn_sig(similar_candidate.def_id).instantiate(tcx, ty_args);
|
||||
let fn_sig = self.instantiate_binder_with_fresh_vars(span, infer::FnCall, fn_sig);
|
||||
if similar_candidate.fn_has_self_parameter {
|
||||
if let Some(args) = args
|
||||
&& fn_sig.inputs()[1..].len() == args.len()
|
||||
{
|
||||
// We found a method with the same number of arguments as the method
|
||||
// call expression the user wrote.
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
msg,
|
||||
similar_candidate.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
// We found a method but either the expression is not a method call or
|
||||
// the argument count didn't match.
|
||||
err.span_help(
|
||||
tcx.def_span(similar_candidate.def_id),
|
||||
format!(
|
||||
"{msg}{}",
|
||||
if let None = args { "" } else { ", but with different arguments" },
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if let Some(args) = args
|
||||
&& fn_sig.inputs().len() == args.len()
|
||||
{
|
||||
// We have fn call expression and the argument count match the associated
|
||||
// function we found.
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
msg,
|
||||
similar_candidate.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
err.span_help(tcx.def_span(similar_candidate.def_id), msg);
|
||||
}
|
||||
} else if let Mode::Path = mode
|
||||
&& args.unwrap_or(&[]).is_empty()
|
||||
{
|
||||
// We have an associated item syntax and we found something that isn't an fn.
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
msg,
|
||||
similar_candidate.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
// The expression is a function or method call, but the item we found is an
|
||||
// associated const or type.
|
||||
err.span_help(tcx.def_span(similar_candidate.def_id), msg);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn confusable_method_name(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: Ident,
|
||||
call_args: Option<Vec<Ty<'tcx>>>,
|
||||
) -> Option<Symbol> {
|
||||
if let ty::Adt(adt, adt_args) = rcvr_ty.kind() {
|
||||
for inherent_impl_did in self.tcx.inherent_impls(adt.did()).into_iter().flatten() {
|
||||
for inherent_method in
|
||||
self.tcx.associated_items(inherent_impl_did).in_definition_order()
|
||||
{
|
||||
if let Some(attr) =
|
||||
self.tcx.get_attr(inherent_method.def_id, sym::rustc_confusables)
|
||||
&& let Some(candidates) = parse_confusables(attr)
|
||||
&& candidates.contains(&item_name.name)
|
||||
&& let ty::AssocKind::Fn = inherent_method.kind
|
||||
{
|
||||
let args =
|
||||
ty::GenericArgs::identity_for_item(self.tcx, inherent_method.def_id)
|
||||
.rebase_onto(
|
||||
self.tcx,
|
||||
inherent_method.container_id(self.tcx),
|
||||
adt_args,
|
||||
);
|
||||
let fn_sig =
|
||||
self.tcx.fn_sig(inherent_method.def_id).instantiate(self.tcx, args);
|
||||
let fn_sig = self.instantiate_binder_with_fresh_vars(
|
||||
item_name.span,
|
||||
infer::FnCall,
|
||||
fn_sig,
|
||||
);
|
||||
if let Some(ref args) = call_args
|
||||
&& fn_sig.inputs()[1..]
|
||||
.iter()
|
||||
.zip(args.into_iter())
|
||||
.all(|(expected, found)| self.can_coerce(*expected, *found))
|
||||
&& fn_sig.inputs()[1..].len() == args.len()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
item_name.span,
|
||||
format!("you might have meant to use `{}`", inherent_method.name),
|
||||
inherent_method.name,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return Some(inherent_method.name);
|
||||
} else if let None = call_args {
|
||||
err.span_note(
|
||||
self.tcx.def_span(inherent_method.def_id),
|
||||
format!(
|
||||
"you might have meant to use method `{}`",
|
||||
inherent_method.name,
|
||||
),
|
||||
);
|
||||
return Some(inherent_method.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn note_candidates_on_method_error(
|
||||
&self,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
|
|
@ -2768,6 +2883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
fn suggest_valid_traits(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
item_name: Ident,
|
||||
valid_out_of_scope_traits: Vec<DefId>,
|
||||
explain: bool,
|
||||
) -> bool {
|
||||
|
|
@ -2786,9 +2902,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err.help("items from traits can only be used if the trait is in scope");
|
||||
}
|
||||
let msg = format!(
|
||||
"the following {traits_are} implemented but not in scope; \
|
||||
perhaps add a `use` for {one_of_them}:",
|
||||
traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" },
|
||||
"{this_trait_is} implemented but not in scope; perhaps you want to import \
|
||||
{one_of_them}",
|
||||
this_trait_is = if candidates.len() == 1 {
|
||||
format!(
|
||||
"trait `{}` which provides `{item_name}` is",
|
||||
self.tcx.item_name(candidates[0]),
|
||||
)
|
||||
} else {
|
||||
format!("the following traits which provide `{item_name}` are")
|
||||
},
|
||||
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
|
||||
);
|
||||
|
||||
|
|
@ -2996,7 +3119,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
if self.suggest_valid_traits(err, valid_out_of_scope_traits, true) {
|
||||
if self.suggest_valid_traits(err, item_name, valid_out_of_scope_traits, true) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -3282,7 +3405,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
[] => {}
|
||||
[trait_info] if trait_info.def_id.is_local() => {
|
||||
if impls_trait(trait_info.def_id) {
|
||||
self.suggest_valid_traits(err, vec![trait_info.def_id], false);
|
||||
self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false);
|
||||
} else {
|
||||
err.subdiagnostic(
|
||||
self.dcx(),
|
||||
|
|
@ -3309,7 +3432,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
));
|
||||
for (i, trait_info) in trait_infos.iter().enumerate() {
|
||||
if impls_trait(trait_info.def_id) {
|
||||
self.suggest_valid_traits(err, vec![trait_info.def_id], false);
|
||||
self.suggest_valid_traits(
|
||||
err,
|
||||
item_name,
|
||||
vec![trait_info.def_id],
|
||||
false,
|
||||
);
|
||||
}
|
||||
msg.push_str(&format!(
|
||||
"\ncandidate #{}: `{}`",
|
||||
|
|
|
|||
|
|
@ -2035,7 +2035,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
slice: Option<&'tcx Pat<'tcx>>,
|
||||
span: Span,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if !slice.is_none() {
|
||||
if slice.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -589,12 +589,16 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
&& 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(
|
||||
if let Ok(d) = hidden_type.build_mismatch_error(
|
||||
&last_opaque_ty,
|
||||
opaque_type_key.def_id,
|
||||
self.tcx(),
|
||||
) {
|
||||
d.stash(
|
||||
self.tcx().def_span(opaque_type_key.def_id),
|
||||
StashKey::OpaqueHiddenTypeMismatch,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,8 +175,12 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
|
|||
),
|
||||
|
||||
ty::ReVar(vid) => {
|
||||
let universe =
|
||||
infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid);
|
||||
let universe = infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.unwrap_region_constraints()
|
||||
.probe_value(vid)
|
||||
.unwrap_err();
|
||||
canonicalizer.canonical_var_for_region(
|
||||
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
|
||||
r,
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ mod note_and_explain;
|
|||
mod suggest;
|
||||
|
||||
pub(crate) mod need_type_info;
|
||||
pub mod sub_relations;
|
||||
pub use need_type_info::TypeAnnotationNeeded;
|
||||
|
||||
pub mod nice_region_error;
|
||||
|
|
@ -123,6 +124,8 @@ fn escape_literal(s: &str) -> String {
|
|||
/// methods which should not be used during the happy path.
|
||||
pub struct TypeErrCtxt<'a, 'tcx> {
|
||||
pub infcx: &'a InferCtxt<'tcx>,
|
||||
pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
|
||||
|
||||
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
|
||||
pub fallback_has_occurred: bool,
|
||||
|
||||
|
|
@ -1935,7 +1938,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
"the full type name has been written to '{}'",
|
||||
path.display(),
|
||||
));
|
||||
diag.note(format!("consider using `--verbose` to print the full type name to the console"));
|
||||
diag.note("consider using `--verbose` to print the full type name to the console");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2434,6 +2437,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
let suggestion =
|
||||
if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
|
||||
suggs.push((sp, suggestion))
|
||||
} else if let GenericKind::Alias(ref p) = bound_kind
|
||||
&& let ty::Projection = p.kind(self.tcx)
|
||||
&& let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
|
||||
&& let Some(ty::ImplTraitInTraitData::Trait { .. }) =
|
||||
self.tcx.opt_rpitit_info(p.def_id)
|
||||
{
|
||||
// The lifetime found in the `impl` is longer than the one on the RPITIT.
|
||||
// Do not suggest `<Type as Trait>::{opaque}: 'static`.
|
||||
} else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
|
||||
let pred = format!("{bound_kind}: {lt_name}");
|
||||
let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
parent_name,
|
||||
});
|
||||
|
||||
let args = if self.infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn)
|
||||
let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
|
||||
== Some(generics_def_id)
|
||||
{
|
||||
"Vec<_>".to_string()
|
||||
|
|
@ -710,7 +710,7 @@ struct InsertableGenericArgs<'tcx> {
|
|||
/// While doing so, the currently best spot is stored in `infer_source`.
|
||||
/// For details on how we rank spots, see [Self::source_cost]
|
||||
struct FindInferSourceVisitor<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
tecx: &'a TypeErrCtxt<'a, 'tcx>,
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
|
||||
target: GenericArg<'tcx>,
|
||||
|
|
@ -722,12 +722,12 @@ struct FindInferSourceVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
||||
fn new(
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
tecx: &'a TypeErrCtxt<'a, 'tcx>,
|
||||
typeck_results: &'a TypeckResults<'tcx>,
|
||||
target: GenericArg<'tcx>,
|
||||
) -> Self {
|
||||
FindInferSourceVisitor {
|
||||
infcx,
|
||||
tecx,
|
||||
typeck_results,
|
||||
|
||||
target,
|
||||
|
|
@ -778,7 +778,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
}
|
||||
|
||||
// The sources are listed in order of preference here.
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tecx.tcx;
|
||||
let ctx = CostCtxt { tcx };
|
||||
match source.kind {
|
||||
InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
|
||||
|
|
@ -829,12 +829,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
|
||||
fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> {
|
||||
let args = self.typeck_results.node_args_opt(hir_id);
|
||||
self.infcx.resolve_vars_if_possible(args)
|
||||
self.tecx.resolve_vars_if_possible(args)
|
||||
}
|
||||
|
||||
fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
|
||||
let ty = self.typeck_results.node_type_opt(hir_id);
|
||||
self.infcx.resolve_vars_if_possible(ty)
|
||||
self.tecx.resolve_vars_if_possible(ty)
|
||||
}
|
||||
|
||||
// Check whether this generic argument is the inference variable we
|
||||
|
|
@ -849,7 +849,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
use ty::{Infer, TyVar};
|
||||
match (inner_ty.kind(), target_ty.kind()) {
|
||||
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
|
||||
self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid)
|
||||
self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
|
@ -857,12 +857,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
(GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
|
||||
use ty::InferConst::*;
|
||||
match (inner_ct.kind(), target_ct.kind()) {
|
||||
(ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.const_unification_table()
|
||||
.unioned(a_vid, b_vid),
|
||||
(ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => {
|
||||
self.tecx.inner.borrow_mut().const_unification_table().unioned(a_vid, b_vid)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -917,7 +914,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tecx.tcx;
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(ref path) => {
|
||||
if let Some(args) = self.node_args_opt(expr.hir_id) {
|
||||
|
|
@ -980,7 +977,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
path: &'tcx hir::Path<'tcx>,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tecx.tcx;
|
||||
let have_turbofish = path.segments.iter().any(|segment| {
|
||||
segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const()))
|
||||
});
|
||||
|
|
@ -1034,7 +1031,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
|||
args: GenericArgsRef<'tcx>,
|
||||
qpath: &'tcx hir::QPath<'tcx>,
|
||||
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tecx.tcx;
|
||||
match qpath {
|
||||
hir::QPath::Resolved(_self_ty, path) => {
|
||||
Box::new(self.resolved_path_inferred_arg_iter(path, args))
|
||||
|
|
@ -1107,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
|||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.infcx.tcx.hir()
|
||||
self.tecx.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
|
||||
|
|
@ -1163,7 +1160,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
|||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
let tcx = self.infcx.tcx;
|
||||
let tcx = self.tecx.tcx;
|
||||
match expr.kind {
|
||||
// When encountering `func(arg)` first look into `arg` and then `func`,
|
||||
// as `arg` is "more specific".
|
||||
|
|
@ -1194,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
|||
if generics.parent.is_none() && generics.has_self {
|
||||
argument_index += 1;
|
||||
}
|
||||
let args = self.infcx.resolve_vars_if_possible(args);
|
||||
let args = self.tecx.resolve_vars_if_possible(args);
|
||||
let generic_args =
|
||||
&generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..];
|
||||
let span = match expr.kind {
|
||||
|
|
@ -1224,7 +1221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
|||
{
|
||||
let output = args.as_closure().sig().output().skip_binder();
|
||||
if self.generic_arg_contains_target(output.into()) {
|
||||
let body = self.infcx.tcx.hir().body(body);
|
||||
let body = self.tecx.tcx.hir().body(body);
|
||||
let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -1252,12 +1249,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
|||
&& let Some(args) = self.node_args_opt(expr.hir_id)
|
||||
&& args.iter().any(|arg| self.generic_arg_contains_target(arg))
|
||||
&& let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
|
||||
&& self.infcx.tcx.trait_of_item(def_id).is_some()
|
||||
&& self.tecx.tcx.trait_of_item(def_id).is_some()
|
||||
&& !has_impl_trait(def_id)
|
||||
{
|
||||
let successor =
|
||||
method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
|
||||
let args = self.infcx.resolve_vars_if_possible(args);
|
||||
let args = self.tecx.resolve_vars_if_possible(args);
|
||||
self.update_infer_source(InferSource {
|
||||
span: path.ident.span,
|
||||
kind: InferSourceKind::FullyQualifiedMethodCall {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::undo_log::NoUndo;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_middle::ty;
|
||||
|
||||
use crate::infer::InferCtxt;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct SubId(u32);
|
||||
impl ut::UnifyKey for SubId {
|
||||
type Value = ();
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
#[inline]
|
||||
fn from_index(i: u32) -> SubId {
|
||||
SubId(i)
|
||||
}
|
||||
fn tag() -> &'static str {
|
||||
"SubId"
|
||||
}
|
||||
}
|
||||
|
||||
/// When reporting ambiguity errors, we sometimes want to
|
||||
/// treat all inference vars which are subtypes of each
|
||||
/// others as if they are equal. For this case we compute
|
||||
/// the transitive closure of our subtype obligations here.
|
||||
///
|
||||
/// E.g. when encountering ambiguity errors, we want to suggest
|
||||
/// specifying some method argument or to add a type annotation
|
||||
/// to a local variable. Because subtyping cannot change the
|
||||
/// shape of a type, it's fine if the cause of the ambiguity error
|
||||
/// is only related to the suggested variable via subtyping.
|
||||
///
|
||||
/// Even for something like `let x = returns_arg(); x.method();` the
|
||||
/// type of `x` is only a supertype of the argument of `returns_arg`. We
|
||||
/// still want to suggest specifying the type of the argument.
|
||||
#[derive(Default)]
|
||||
pub struct SubRelations {
|
||||
map: FxHashMap<ty::TyVid, SubId>,
|
||||
table: ut::UnificationTableStorage<SubId>,
|
||||
}
|
||||
|
||||
impl SubRelations {
|
||||
fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
|
||||
let root_vid = infcx.root_var(vid);
|
||||
*self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
|
||||
}
|
||||
|
||||
pub fn add_constraints<'tcx>(
|
||||
&mut self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
|
||||
) {
|
||||
for p in obls {
|
||||
let (a, b) = match p.kind().skip_binder() {
|
||||
ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
|
||||
(a, b)
|
||||
}
|
||||
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
match (a.kind(), b.kind()) {
|
||||
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
|
||||
let a = self.get_id(infcx, a_vid);
|
||||
let b = self.get_id(infcx, b_vid);
|
||||
self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
|
||||
let a = self.get_id(infcx, a);
|
||||
let b = self.get_id(infcx, b);
|
||||
self.table.with_log(&mut NoUndo).unioned(a, b)
|
||||
}
|
||||
}
|
||||
|
|
@ -56,49 +56,46 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn freshen_ty<F>(&mut self, opt_ty: Option<Ty<'tcx>>, key: ty::InferTy, mk_fresh: F) -> Ty<'tcx>
|
||||
fn freshen_ty<F>(&mut self, input: Result<Ty<'tcx>, ty::InferTy>, mk_fresh: F) -> Ty<'tcx>
|
||||
where
|
||||
F: FnOnce(u32) -> Ty<'tcx>,
|
||||
{
|
||||
if let Some(ty) = opt_ty {
|
||||
return ty.fold_with(self);
|
||||
}
|
||||
|
||||
match self.ty_freshen_map.entry(key) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let index = self.ty_freshen_count;
|
||||
self.ty_freshen_count += 1;
|
||||
let t = mk_fresh(index);
|
||||
entry.insert(t);
|
||||
t
|
||||
}
|
||||
match input {
|
||||
Ok(ty) => ty.fold_with(self),
|
||||
Err(key) => match self.ty_freshen_map.entry(key) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let index = self.ty_freshen_count;
|
||||
self.ty_freshen_count += 1;
|
||||
let t = mk_fresh(index);
|
||||
entry.insert(t);
|
||||
t
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn freshen_const<F>(
|
||||
&mut self,
|
||||
opt_ct: Option<ty::Const<'tcx>>,
|
||||
key: ty::InferConst,
|
||||
input: Result<ty::Const<'tcx>, ty::InferConst>,
|
||||
freshener: F,
|
||||
ty: Ty<'tcx>,
|
||||
) -> ty::Const<'tcx>
|
||||
where
|
||||
F: FnOnce(u32) -> ty::InferConst,
|
||||
{
|
||||
if let Some(ct) = opt_ct {
|
||||
return ct.fold_with(self);
|
||||
}
|
||||
|
||||
match self.const_freshen_map.entry(key) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let index = self.const_freshen_count;
|
||||
self.const_freshen_count += 1;
|
||||
let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
|
||||
entry.insert(ct);
|
||||
ct
|
||||
}
|
||||
match input {
|
||||
Ok(ct) => ct.fold_with(self),
|
||||
Err(key) => match self.const_freshen_map.entry(key) {
|
||||
Entry::Occupied(entry) => *entry.get(),
|
||||
Entry::Vacant(entry) => {
|
||||
let index = self.const_freshen_count;
|
||||
self.const_freshen_count += 1;
|
||||
let ct = ty::Const::new_infer(self.infcx.tcx, freshener(index), ty);
|
||||
entry.insert(ct);
|
||||
ct
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -146,19 +143,22 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
|
|||
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
|
||||
match ct.kind() {
|
||||
ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
|
||||
let opt_ct =
|
||||
self.infcx.inner.borrow_mut().const_unification_table().probe_value(v).known();
|
||||
self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty())
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let input =
|
||||
inner.const_unification_table().probe_value(v).known().ok_or_else(|| {
|
||||
ty::InferConst::Var(inner.const_unification_table().find(v).vid)
|
||||
});
|
||||
drop(inner);
|
||||
self.freshen_const(input, ty::InferConst::Fresh, ct.ty())
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::EffectVar(v)) => {
|
||||
let opt_ct =
|
||||
self.infcx.inner.borrow_mut().effect_unification_table().probe_value(v).known();
|
||||
self.freshen_const(
|
||||
opt_ct,
|
||||
ty::InferConst::EffectVar(v),
|
||||
ty::InferConst::Fresh,
|
||||
ct.ty(),
|
||||
)
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let input =
|
||||
inner.effect_unification_table().probe_value(v).known().ok_or_else(|| {
|
||||
ty::InferConst::EffectVar(inner.effect_unification_table().find(v).vid)
|
||||
});
|
||||
drop(inner);
|
||||
self.freshen_const(input, ty::InferConst::Fresh, ct.ty())
|
||||
}
|
||||
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
|
||||
if i >= self.const_freshen_count {
|
||||
|
|
@ -191,35 +191,37 @@ impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
|
|||
fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option<Ty<'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| Ty::new_fresh(self.infcx.tcx, n)))
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let input = inner
|
||||
.type_variables()
|
||||
.probe(v)
|
||||
.known()
|
||||
.ok_or_else(|| ty::TyVar(inner.type_variables().root_var(v)));
|
||||
drop(inner);
|
||||
Some(self.freshen_ty(input, |n| Ty::new_fresh(self.infcx.tcx, n)))
|
||||
}
|
||||
|
||||
ty::IntVar(v) => Some(
|
||||
self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
ty::IntVar(v),
|
||||
|n| Ty::new_fresh_int(self.infcx.tcx, n),
|
||||
),
|
||||
),
|
||||
ty::IntVar(v) => {
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let input = inner
|
||||
.int_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx))
|
||||
.ok_or_else(|| ty::IntVar(inner.int_unification_table().find(v)));
|
||||
drop(inner);
|
||||
Some(self.freshen_ty(input, |n| Ty::new_fresh_int(self.infcx.tcx, n)))
|
||||
}
|
||||
|
||||
ty::FloatVar(v) => Some(
|
||||
self.freshen_ty(
|
||||
self.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx)),
|
||||
ty::FloatVar(v),
|
||||
|n| Ty::new_fresh_float(self.infcx.tcx, n),
|
||||
),
|
||||
),
|
||||
ty::FloatVar(v) => {
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let input = inner
|
||||
.float_unification_table()
|
||||
.probe_value(v)
|
||||
.map(|v| v.to_type(self.infcx.tcx))
|
||||
.ok_or_else(|| ty::FloatVar(inner.float_unification_table().find(v)));
|
||||
drop(inner);
|
||||
Some(self.freshen_ty(input, |n| Ty::new_fresh_float(self.infcx.tcx, n)))
|
||||
}
|
||||
|
||||
ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => {
|
||||
if ct >= self.ty_freshen_count {
|
||||
|
|
|
|||
|
|
@ -374,7 +374,10 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
|
|||
}
|
||||
|
||||
fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
|
||||
Some(self.universe_of_region_vid(lt))
|
||||
match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) {
|
||||
Err(universe) => Some(universe),
|
||||
Ok(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn root_ty_var(&self, vid: TyVid) -> TyVid {
|
||||
|
|
@ -762,6 +765,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
|
||||
TypeErrCtxt {
|
||||
infcx: self,
|
||||
sub_relations: Default::default(),
|
||||
typeck_results: None,
|
||||
fallback_has_occurred: false,
|
||||
normalize_fn_sig: Box::new(|fn_sig| fn_sig),
|
||||
|
|
@ -1029,7 +1033,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
let r_b = self.shallow_resolve(predicate.skip_binder().b);
|
||||
match (r_a.kind(), r_b.kind()) {
|
||||
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
|
||||
self.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
|
||||
return Err((a_vid, b_vid));
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -1155,11 +1158,6 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
self.inner.borrow_mut().unwrap_region_constraints().universe(r)
|
||||
}
|
||||
|
||||
/// Return the universe that the region variable `r` was created in.
|
||||
pub fn universe_of_region_vid(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
|
||||
}
|
||||
|
||||
/// Number of region variables created so far.
|
||||
pub fn num_region_vars(&self) -> usize {
|
||||
self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()
|
||||
|
|
|
|||
|
|
@ -90,11 +90,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
struct LeakCheck<'me, 'tcx> {
|
||||
struct LeakCheck<'a, 'b, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
outer_universe: ty::UniverseIndex,
|
||||
mini_graph: &'me MiniGraph<'tcx>,
|
||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||
mini_graph: &'a MiniGraph<'tcx>,
|
||||
rcc: &'a mut RegionConstraintCollector<'b, 'tcx>,
|
||||
|
||||
// Initially, for each SCC S, stores a placeholder `P` such that `S = P`
|
||||
// must hold.
|
||||
|
|
@ -117,13 +117,13 @@ struct LeakCheck<'me, 'tcx> {
|
|||
scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
|
||||
impl<'a, 'b, 'tcx> LeakCheck<'a, 'b, 'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
outer_universe: ty::UniverseIndex,
|
||||
max_universe: ty::UniverseIndex,
|
||||
mini_graph: &'me MiniGraph<'tcx>,
|
||||
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
|
||||
mini_graph: &'a MiniGraph<'tcx>,
|
||||
rcc: &'a mut RegionConstraintCollector<'b, 'tcx>,
|
||||
) -> Self {
|
||||
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
|
||||
Self {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,11 @@ use super::{
|
|||
};
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::intern::Interned;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_data_structures::undo_log::UndoLogs;
|
||||
use rustc_data_structures::unify as ut;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
|
||||
use rustc_middle::infer::unify_key::{RegionVariableValue, RegionVidKey};
|
||||
use rustc_middle::ty::ReStatic;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ReBound, ReVar};
|
||||
|
|
@ -292,6 +291,18 @@ type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>;
|
|||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RegionVariableInfo {
|
||||
pub origin: RegionVariableOrigin,
|
||||
// FIXME: This is only necessary for `fn take_and_reset_data` and
|
||||
// `lexical_region_resolve`. We should rework `lexical_region_resolve`
|
||||
// in the near/medium future anyways and could move the unverse info
|
||||
// for `fn take_and_reset_data` into a separate table which is
|
||||
// only populated when needed.
|
||||
//
|
||||
// For both of these cases it is fine that this can diverge from the
|
||||
// actual universe of the variable, which is directly stored in the
|
||||
// unification table for unknown region variables. At some point we could
|
||||
// stop emitting bidirectional outlives constraints if equate succeeds.
|
||||
// This would be currently unsound as it would cause us to drop the universe
|
||||
// changes in `lexical_region_resolve`.
|
||||
pub universe: ty::UniverseIndex,
|
||||
}
|
||||
|
||||
|
|
@ -395,7 +406,11 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
// `RegionConstraintData` contains the relationship here.
|
||||
if *any_unifications {
|
||||
*any_unifications = false;
|
||||
self.unification_table_mut().reset_unifications(|_| UnifiedRegion::new(None));
|
||||
// Manually inlined `self.unification_table_mut()` as `self` is used in the closure.
|
||||
ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log)
|
||||
.reset_unifications(|key| RegionVariableValue::Unknown {
|
||||
universe: self.storage.var_infos[key.vid].universe,
|
||||
});
|
||||
}
|
||||
|
||||
data
|
||||
|
|
@ -422,18 +437,13 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
) -> RegionVid {
|
||||
let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
|
||||
|
||||
let u_vid = self.unification_table_mut().new_key(UnifiedRegion::new(None));
|
||||
let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe });
|
||||
assert_eq!(vid, u_vid.vid);
|
||||
self.undo_log.push(AddVar(vid));
|
||||
debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
|
||||
vid
|
||||
}
|
||||
|
||||
/// Returns the universe for the given variable.
|
||||
pub(super) fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex {
|
||||
self.var_infos[vid].universe
|
||||
}
|
||||
|
||||
/// Returns the origin for the given variable.
|
||||
pub(super) fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin {
|
||||
self.var_infos[vid].origin
|
||||
|
|
@ -467,26 +477,41 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
pub(super) fn make_eqregion(
|
||||
&mut self,
|
||||
origin: SubregionOrigin<'tcx>,
|
||||
sub: Region<'tcx>,
|
||||
sup: Region<'tcx>,
|
||||
a: Region<'tcx>,
|
||||
b: Region<'tcx>,
|
||||
) {
|
||||
if sub != sup {
|
||||
if a != b {
|
||||
// Eventually, it would be nice to add direct support for
|
||||
// equating regions.
|
||||
self.make_subregion(origin.clone(), sub, sup);
|
||||
self.make_subregion(origin, sup, sub);
|
||||
self.make_subregion(origin.clone(), a, b);
|
||||
self.make_subregion(origin, b, a);
|
||||
|
||||
match (sub, sup) {
|
||||
(Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
|
||||
self.unification_table_mut().union(*sub, *sup);
|
||||
self.any_unifications = true;
|
||||
match (a.kind(), b.kind()) {
|
||||
(ty::ReVar(a), ty::ReVar(b)) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", a, b);
|
||||
if self.unification_table_mut().unify_var_var(a, b).is_ok() {
|
||||
self.any_unifications = true;
|
||||
}
|
||||
}
|
||||
(Region(Interned(ReVar(vid), _)), value)
|
||||
| (value, Region(Interned(ReVar(vid), _))) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
|
||||
self.unification_table_mut().union_value(*vid, UnifiedRegion::new(Some(value)));
|
||||
self.any_unifications = true;
|
||||
(ty::ReVar(vid), _) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", vid, b);
|
||||
if self
|
||||
.unification_table_mut()
|
||||
.unify_var_value(vid, RegionVariableValue::Known { value: b })
|
||||
.is_ok()
|
||||
{
|
||||
self.any_unifications = true;
|
||||
};
|
||||
}
|
||||
(_, ty::ReVar(vid)) => {
|
||||
debug!("make_eqregion: unifying {:?} with {:?}", a, vid);
|
||||
if self
|
||||
.unification_table_mut()
|
||||
.unify_var_value(vid, RegionVariableValue::Known { value: a })
|
||||
.is_ok()
|
||||
{
|
||||
self.any_unifications = true;
|
||||
};
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
|
|
@ -603,18 +628,21 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
tcx: TyCtxt<'tcx>,
|
||||
vid: ty::RegionVid,
|
||||
) -> ty::Region<'tcx> {
|
||||
let mut ut = self.unification_table_mut(); // FIXME(rust-lang/ena#42): unnecessary mut
|
||||
let mut ut = self.unification_table_mut();
|
||||
let root_vid = ut.find(vid).vid;
|
||||
let resolved = ut
|
||||
.probe_value(root_vid)
|
||||
.get_value_ignoring_universes()
|
||||
.unwrap_or_else(|| ty::Region::new_var(tcx, root_vid));
|
||||
match ut.probe_value(root_vid) {
|
||||
RegionVariableValue::Known { value } => value,
|
||||
RegionVariableValue::Unknown { .. } => 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 {
|
||||
ty::Region::new_var(tcx, vid)
|
||||
pub fn probe_value(
|
||||
&mut self,
|
||||
vid: ty::RegionVid,
|
||||
) -> Result<ty::Region<'tcx>, ty::UniverseIndex> {
|
||||
match self.unification_table_mut().probe_value(vid) {
|
||||
RegionVariableValue::Known { value } => Ok(value),
|
||||
RegionVariableValue::Unknown { universe } => Err(universe),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -654,7 +682,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
new_r
|
||||
}
|
||||
|
||||
pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex {
|
||||
pub fn universe(&mut self, region: Region<'tcx>) -> ty::UniverseIndex {
|
||||
match *region {
|
||||
ty::ReStatic
|
||||
| ty::ReErased
|
||||
|
|
@ -662,7 +690,10 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
| ty::ReEarlyParam(..)
|
||||
| ty::ReError(_) => ty::UniverseIndex::ROOT,
|
||||
ty::RePlaceholder(placeholder) => placeholder.universe,
|
||||
ty::ReVar(vid) => self.var_universe(vid),
|
||||
ty::ReVar(vid) => match self.probe_value(vid) {
|
||||
Ok(value) => self.universe(value),
|
||||
Err(universe) => universe,
|
||||
},
|
||||
ty::ReBound(..) => bug!("universe(): encountered bound region {:?}", region),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// This is *not* expected to be used anywhere except for an implementation of
|
||||
/// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
|
||||
/// other usecases (i.e. setting the value of a type var).
|
||||
#[instrument(level = "debug", skip(self, relation, target_is_expected))]
|
||||
#[instrument(level = "debug", skip(self, relation))]
|
||||
pub fn instantiate_ty_var<R: ObligationEmittingRelation<'tcx>>(
|
||||
&self,
|
||||
relation: &mut R,
|
||||
target_is_expected: bool,
|
||||
target_vid: ty::TyVid,
|
||||
ambient_variance: ty::Variance,
|
||||
instantiation_variance: ty::Variance,
|
||||
source_ty: Ty<'tcx>,
|
||||
) -> RelateResult<'tcx, ()> {
|
||||
debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown());
|
||||
|
|
@ -46,7 +46,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
//
|
||||
// We then relate `generalized_ty <: source_ty`,adding constraints like `'x: '?2` and `?1 <: ?3`.
|
||||
let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } =
|
||||
self.generalize(relation.span(), target_vid, ambient_variance, source_ty)?;
|
||||
self.generalize(relation.span(), target_vid, instantiation_variance, source_ty)?;
|
||||
|
||||
// Constrain `b_vid` to the generalized type `generalized_ty`.
|
||||
if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() {
|
||||
|
|
@ -73,7 +73,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
// the alias can be normalized to something which does not
|
||||
// mention `?0`.
|
||||
if self.next_trait_solver() {
|
||||
let (lhs, rhs, direction) = match ambient_variance {
|
||||
let (lhs, rhs, direction) = match instantiation_variance {
|
||||
ty::Variance::Invariant => {
|
||||
(generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate)
|
||||
}
|
||||
|
|
@ -106,22 +106,28 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// HACK: make sure that we `a_is_expected` continues to be
|
||||
// correct when relating the generalized type with the source.
|
||||
// NOTE: The `instantiation_variance` is not the same variance as
|
||||
// used by the relation. When instantiating `b`, `target_is_expected`
|
||||
// is flipped and the `instantion_variance` is also flipped. To
|
||||
// constrain the `generalized_ty` while using the original relation,
|
||||
// we therefore only have to flip the arguments.
|
||||
//
|
||||
// ```ignore (not code)
|
||||
// ?a rel B
|
||||
// instantiate_ty_var(?a, B) # expected and variance not flipped
|
||||
// B' rel B
|
||||
// ```
|
||||
// or
|
||||
// ```ignore (not code)
|
||||
// A rel ?b
|
||||
// instantiate_ty_var(?b, A) # expected and variance flipped
|
||||
// A rel A'
|
||||
// ```
|
||||
if target_is_expected == relation.a_is_expected() {
|
||||
relation.relate_with_variance(
|
||||
ambient_variance,
|
||||
ty::VarianceDiagInfo::default(),
|
||||
generalized_ty,
|
||||
source_ty,
|
||||
)?;
|
||||
relation.relate(generalized_ty, source_ty)?;
|
||||
} else {
|
||||
relation.relate_with_variance(
|
||||
ambient_variance.xform(ty::Contravariant),
|
||||
ty::VarianceDiagInfo::default(),
|
||||
source_ty,
|
||||
generalized_ty,
|
||||
)?;
|
||||
debug!("flip relation");
|
||||
relation.relate(source_ty, generalized_ty)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -217,10 +223,9 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
) -> RelateResult<'tcx, Generalization<T>> {
|
||||
assert!(!source_term.has_escaping_bound_vars());
|
||||
let (for_universe, root_vid) = match target_vid.into() {
|
||||
ty::TermVid::Ty(ty_vid) => (
|
||||
self.probe_ty_var(ty_vid).unwrap_err(),
|
||||
ty::TermVid::Ty(self.inner.borrow_mut().type_variables().sub_root_var(ty_vid)),
|
||||
),
|
||||
ty::TermVid::Ty(ty_vid) => {
|
||||
(self.probe_ty_var(ty_vid).unwrap_err(), ty::TermVid::Ty(self.root_var(ty_vid)))
|
||||
}
|
||||
ty::TermVid::Const(ct_vid) => (
|
||||
self.probe_const_var(ct_vid).unwrap_err(),
|
||||
ty::TermVid::Const(
|
||||
|
|
@ -424,9 +429,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
|
|||
ty::Infer(ty::TyVar(vid)) => {
|
||||
let mut inner = self.infcx.inner.borrow_mut();
|
||||
let vid = inner.type_variables().root_var(vid);
|
||||
let sub_vid = inner.type_variables().sub_root_var(vid);
|
||||
|
||||
if ty::TermVid::Ty(sub_vid) == self.root_vid {
|
||||
if ty::TermVid::Ty(vid) == self.root_vid {
|
||||
// If sub-roots are equal, then `root_vid` and
|
||||
// `vid` are related via subtyping.
|
||||
Err(self.cyclic_term_error())
|
||||
|
|
@ -461,11 +464,6 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
|
|||
let new_var_id =
|
||||
inner.type_variables().new_var(self.for_universe, origin);
|
||||
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
|
||||
// docs in the `type_variables` module.
|
||||
inner.type_variables().sub(vid, new_var_id);
|
||||
debug!("replacing original vid={:?} with new={:?}", vid, u);
|
||||
Ok(u)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use rustc_data_structures::undo_log::Rollback;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::ty::{self, Ty, TyVid};
|
||||
|
|
@ -12,35 +13,9 @@ use std::cmp;
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::Range;
|
||||
|
||||
use rustc_data_structures::undo_log::Rollback;
|
||||
|
||||
/// Represents a single undo-able action that affects a type inference variable.
|
||||
#[derive(Clone)]
|
||||
pub(crate) enum UndoLog<'tcx> {
|
||||
EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
|
||||
SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
|
||||
}
|
||||
|
||||
/// Convert from a specific kind of undo to the more general UndoLog
|
||||
impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
|
||||
fn from(l: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) -> Self {
|
||||
UndoLog::EqRelation(l)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a specific kind of undo to the more general UndoLog
|
||||
impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> {
|
||||
fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self {
|
||||
UndoLog::SubRelation(l)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
|
||||
fn reverse(&mut self, undo: UndoLog<'tcx>) {
|
||||
match undo {
|
||||
UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo),
|
||||
UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo),
|
||||
}
|
||||
impl<'tcx> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for TypeVariableStorage<'tcx> {
|
||||
fn reverse(&mut self, undo: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) {
|
||||
self.eq_relations.reverse(undo)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,41 +27,6 @@ pub struct TypeVariableStorage<'tcx> {
|
|||
/// constraint `?X == ?Y`. This table also stores, for each key,
|
||||
/// the known value.
|
||||
eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>,
|
||||
|
||||
/// Two variables are unified in `sub_relations` when we have a
|
||||
/// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
|
||||
/// table exists only to help with the occurs check. In particular,
|
||||
/// we want to report constraints like these as an occurs check
|
||||
/// violation:
|
||||
/// ``` text
|
||||
/// ?1 <: ?3
|
||||
/// Box<?3> <: ?1
|
||||
/// ```
|
||||
/// Without this second table, what would happen in a case like
|
||||
/// this is that we would instantiate `?1` with a generalized
|
||||
/// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>`
|
||||
/// and infer that `?3 <: ?6`. Next, since `?1` was instantiated,
|
||||
/// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`,
|
||||
/// and instantiate `?3` with `Box<?9>`. Finally, we would relate
|
||||
/// `?6 <: ?9`. But now that we instantiated `?3`, we can process
|
||||
/// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle
|
||||
/// continues. (This is `occurs-check-2.rs`.)
|
||||
///
|
||||
/// What prevents this cycle is that when we generalize
|
||||
/// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6`
|
||||
/// (in the generalizer). When we then process `Box<?6> <: ?3`,
|
||||
/// the occurs check then fails because `?6` and `?3` are sub-unified,
|
||||
/// and hence generalization fails.
|
||||
///
|
||||
/// This is reasonable because, in Rust, subtypes have the same
|
||||
/// "skeleton" and hence there is no possible type such that
|
||||
/// (e.g.) `Box<?3> <: ?3` for any `?3`.
|
||||
///
|
||||
/// In practice, we sometimes sub-unify variables in other spots, such
|
||||
/// as when processing subtype predicates. This is not necessary but is
|
||||
/// done to aid diagnostics, as it allows us to be more effective when
|
||||
/// we guide the user towards where they should insert type hints.
|
||||
sub_relations: ut::UnificationTableStorage<ty::TyVid>,
|
||||
}
|
||||
|
||||
pub struct TypeVariableTable<'a, 'tcx> {
|
||||
|
|
@ -158,7 +98,6 @@ impl<'tcx> TypeVariableStorage<'tcx> {
|
|||
TypeVariableStorage {
|
||||
values: Default::default(),
|
||||
eq_relations: ut::UnificationTableStorage::new(),
|
||||
sub_relations: ut::UnificationTableStorage::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -197,16 +136,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
debug_assert!(self.probe(a).is_unknown());
|
||||
debug_assert!(self.probe(b).is_unknown());
|
||||
self.eq_relations().union(a, b);
|
||||
self.sub_relations().union(a, b);
|
||||
}
|
||||
|
||||
/// Records that `a <: b`, depending on `dir`.
|
||||
///
|
||||
/// Precondition: neither `a` nor `b` are known.
|
||||
pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
|
||||
debug_assert!(self.probe(a).is_unknown());
|
||||
debug_assert!(self.probe(b).is_unknown());
|
||||
self.sub_relations().union(a, b);
|
||||
}
|
||||
|
||||
/// Instantiates `vid` with the type `ty`.
|
||||
|
|
@ -240,10 +169,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
origin: TypeVariableOrigin,
|
||||
) -> ty::TyVid {
|
||||
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
|
||||
|
||||
let sub_key = self.sub_relations().new_key(());
|
||||
debug_assert_eq!(eq_key.vid, sub_key);
|
||||
|
||||
let index = self.storage.values.push(TypeVariableData { origin });
|
||||
debug_assert_eq!(eq_key.vid, index);
|
||||
|
||||
|
|
@ -266,24 +191,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
self.eq_relations().find(vid).vid
|
||||
}
|
||||
|
||||
/// Returns the "root" variable of `vid` in the `sub_relations`
|
||||
/// equivalence table. All type variables that have been are
|
||||
/// related via equality or subtyping will yield the same root
|
||||
/// variable (per the union-find algorithm), so `sub_root_var(a)
|
||||
/// == sub_root_var(b)` implies that:
|
||||
/// ```text
|
||||
/// exists X. (a <: X || X <: a) && (b <: X || X <: b)
|
||||
/// ```
|
||||
pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
|
||||
self.sub_relations().find(vid)
|
||||
}
|
||||
|
||||
/// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some
|
||||
/// type X such that `forall i in {a, b}. (i <: X || X <: i)`.
|
||||
pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool {
|
||||
self.sub_root_var(a) == self.sub_root_var(b)
|
||||
}
|
||||
|
||||
/// Retrieves the type to which `vid` has been instantiated, if
|
||||
/// any.
|
||||
pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> {
|
||||
|
|
@ -314,11 +221,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
self.storage.eq_relations.with_log(self.undo_log)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
|
||||
self.storage.sub_relations.with_log(self.undo_log)
|
||||
}
|
||||
|
||||
/// Returns a range of the type variables created during the snapshot.
|
||||
pub fn vars_since_snapshot(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub struct Snapshot<'tcx> {
|
|||
#[derive(Clone)]
|
||||
pub(crate) enum UndoLog<'tcx> {
|
||||
OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>),
|
||||
TypeVariables(type_variable::UndoLog<'tcx>),
|
||||
TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
|
||||
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
|
||||
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
|
||||
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
|
||||
|
|
@ -46,17 +46,13 @@ macro_rules! impl_from {
|
|||
// Upcast from a single kind of "undoable action" to the general enum
|
||||
impl_from! {
|
||||
RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
|
||||
TypeVariables(type_variable::UndoLog<'tcx>),
|
||||
|
||||
TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
|
||||
TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
|
||||
|
||||
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
|
||||
|
||||
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
|
||||
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
|
||||
|
||||
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
|
||||
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
|
||||
|
||||
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
|
||||
ProjectionCache(traits::UndoLog<'tcx>),
|
||||
|
|
|
|||
|
|
@ -429,6 +429,7 @@ lint_non_upper_case_global = {$sort} `{$name}` should have an upper case name
|
|||
lint_noop_method_call = call to `.{$method}()` on a reference in this situation does nothing
|
||||
.suggestion = remove this redundant call
|
||||
.note = the type `{$orig_ty}` does not implement `{$trait_}`, so calling `{$method}` on `&{$orig_ty}` copies the reference, which does not do anything and can be removed
|
||||
.derive_suggestion = if you meant to clone `{$orig_ty}`, implement `Clone` for it
|
||||
|
||||
lint_only_cast_u8_to_char = only `u8` can be cast into `char`
|
||||
.suggestion = use a `char` literal instead
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,12 @@ pub struct NoopMethodCallDiag<'a> {
|
|||
pub trait_: Symbol,
|
||||
#[suggestion(code = "", applicability = "machine-applicable")]
|
||||
pub label: Span,
|
||||
#[suggestion(
|
||||
lint_derive_suggestion,
|
||||
code = "#[derive(Clone)]\n",
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub suggest_derive: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
|
|
|
|||
|
|
@ -121,10 +121,20 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
|
|||
let orig_ty = expr_ty.peel_refs();
|
||||
|
||||
if receiver_ty == expr_ty {
|
||||
let suggest_derive = match orig_ty.kind() {
|
||||
ty::Adt(def, _) => Some(cx.tcx.def_span(def.did()).shrink_to_lo()),
|
||||
_ => None,
|
||||
};
|
||||
cx.emit_span_lint(
|
||||
NOOP_METHOD_CALL,
|
||||
span,
|
||||
NoopMethodCallDiag { method: call.ident.name, orig_ty, trait_, label: span },
|
||||
NoopMethodCallDiag {
|
||||
method: call.ident.name,
|
||||
orig_ty,
|
||||
trait_,
|
||||
label: span,
|
||||
suggest_derive,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
match name {
|
||||
|
|
|
|||
|
|
@ -450,6 +450,20 @@ extern "C" void LLVMRustSetAlgebraicMath(LLVMValueRef V) {
|
|||
}
|
||||
}
|
||||
|
||||
// Enable the reassoc fast-math flag, allowing transformations that pretend
|
||||
// floating-point addition and multiplication are associative.
|
||||
//
|
||||
// Note that this does NOT enable any flags which can cause a floating-point operation on
|
||||
// well-defined inputs to return poison, and therefore this function can be used to build
|
||||
// safe Rust intrinsics (such as fadd_algebraic).
|
||||
//
|
||||
// https://llvm.org/docs/LangRef.html#fast-math-flags
|
||||
extern "C" void LLVMRustSetAllowReassoc(LLVMValueRef V) {
|
||||
if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
|
||||
I->setHasAllowReassoc(true);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" LLVMValueRef
|
||||
LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Source,
|
||||
const char *Name, LLVMAtomicOrdering Order) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ty::{self, Region, Ty, TyCtxt};
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
use rustc_data_structures::unify::{NoError, UnifyKey, UnifyValue};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
|
@ -10,26 +10,16 @@ pub trait ToType {
|
|||
fn to_type<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct UnifiedRegion<'tcx> {
|
||||
value: Option<ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> UnifiedRegion<'tcx> {
|
||||
pub fn new(value: Option<Region<'tcx>>) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
|
||||
/// The caller is responsible for checking universe compatibility before using this value.
|
||||
pub fn get_value_ignoring_universes(self) -> Option<Region<'tcx>> {
|
||||
self.value
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RegionVariableValue<'tcx> {
|
||||
Known { value: ty::Region<'tcx> },
|
||||
Unknown { universe: ty::UniverseIndex },
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Copy, Clone, Debug)]
|
||||
pub struct RegionVidKey<'tcx> {
|
||||
pub vid: ty::RegionVid,
|
||||
pub phantom: PhantomData<UnifiedRegion<'tcx>>,
|
||||
pub phantom: PhantomData<RegionVariableValue<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> From<ty::RegionVid> for RegionVidKey<'tcx> {
|
||||
|
|
@ -39,7 +29,7 @@ impl<'tcx> From<ty::RegionVid> for RegionVidKey<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> UnifyKey for RegionVidKey<'tcx> {
|
||||
type Value = UnifiedRegion<'tcx>;
|
||||
type Value = RegionVariableValue<'tcx>;
|
||||
#[inline]
|
||||
fn index(&self) -> u32 {
|
||||
self.vid.as_u32()
|
||||
|
|
@ -53,36 +43,47 @@ impl<'tcx> UnifyKey for RegionVidKey<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> UnifyValue for UnifiedRegion<'tcx> {
|
||||
type Error = NoError;
|
||||
pub struct RegionUnificationError;
|
||||
impl<'tcx> UnifyValue for RegionVariableValue<'tcx> {
|
||||
type Error = RegionUnificationError;
|
||||
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
|
||||
// We pick the value of the least universe because it is compatible with more variables.
|
||||
// This is *not* necessary for completeness.
|
||||
#[cold]
|
||||
fn min_universe<'tcx>(r1: Region<'tcx>, r2: Region<'tcx>) -> Region<'tcx> {
|
||||
cmp::min_by_key(r1, r2, |r| match r.kind() {
|
||||
ty::ReStatic
|
||||
| ty::ReErased
|
||||
| ty::ReLateParam(..)
|
||||
| ty::ReEarlyParam(..)
|
||||
| ty::ReError(_) => ty::UniverseIndex::ROOT,
|
||||
ty::RePlaceholder(placeholder) => placeholder.universe,
|
||||
ty::ReVar(..) | ty::ReBound(..) => bug!("not a universal region"),
|
||||
})
|
||||
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
|
||||
match (*value1, *value2) {
|
||||
(RegionVariableValue::Known { .. }, RegionVariableValue::Known { .. }) => {
|
||||
Err(RegionUnificationError)
|
||||
}
|
||||
|
||||
(RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe })
|
||||
| (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => {
|
||||
let universe_of_value = match value.kind() {
|
||||
ty::ReStatic
|
||||
| ty::ReErased
|
||||
| ty::ReLateParam(..)
|
||||
| ty::ReEarlyParam(..)
|
||||
| ty::ReError(_) => ty::UniverseIndex::ROOT,
|
||||
ty::RePlaceholder(placeholder) => placeholder.universe,
|
||||
ty::ReVar(..) | ty::ReBound(..) => bug!("not a universal region"),
|
||||
};
|
||||
|
||||
if universe.can_name(universe_of_value) {
|
||||
Ok(RegionVariableValue::Known { value })
|
||||
} else {
|
||||
Err(RegionUnificationError)
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
RegionVariableValue::Unknown { universe: a },
|
||||
RegionVariableValue::Unknown { universe: b },
|
||||
) => {
|
||||
// If we unify two unconstrained regions then whatever
|
||||
// value they wind up taking (which must be the same value) must
|
||||
// be nameable by both universes. Therefore, the resulting
|
||||
// universe is the minimum of the two universes, because that is
|
||||
// the one which contains the fewest names in scope.
|
||||
Ok(RegionVariableValue::Unknown { universe: a.min(b) })
|
||||
}
|
||||
}
|
||||
|
||||
Ok(match (value1.value, value2.value) {
|
||||
// Here we can just pick one value, because the full constraints graph
|
||||
// will be handled later. Ideally, we might want a `MultipleValues`
|
||||
// variant or something. For now though, this is fine.
|
||||
(Some(val1), Some(val2)) => Self { value: Some(min_universe(val1, val2)) },
|
||||
|
||||
(Some(_), _) => *value1,
|
||||
(_, Some(_)) => *value2,
|
||||
|
||||
(None, None) => *value1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![doc(rust_logo)]
|
||||
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
|
||||
#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(array_windows)]
|
||||
|
|
@ -32,7 +34,6 @@
|
|||
#![feature(core_intrinsics)]
|
||||
#![feature(const_type_name)]
|
||||
#![feature(discriminant_kind)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(coroutines)]
|
||||
#![feature(generic_nonzero)]
|
||||
#![feature(if_let_guard)]
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ impl<'sess> OnDiskCache<'sess> {
|
|||
|
||||
for (index, file) in files.iter().enumerate() {
|
||||
let index = SourceFileIndex(index as u32);
|
||||
let file_ptr: *const SourceFile = &**file as *const _;
|
||||
let file_ptr: *const SourceFile = std::ptr::addr_of!(**file);
|
||||
file_to_file_index.insert(file_ptr, index);
|
||||
let source_file_id = EncodedSourceFileId::new(tcx, file);
|
||||
file_index_to_stable_id.insert(index, source_file_id);
|
||||
|
|
@ -835,7 +835,7 @@ pub struct CacheEncoder<'a, 'tcx> {
|
|||
impl<'a, 'tcx> CacheEncoder<'a, 'tcx> {
|
||||
#[inline]
|
||||
fn source_file_index(&mut self, source_file: Lrc<SourceFile>) -> SourceFileIndex {
|
||||
self.file_to_file_index[&(&*source_file as *const SourceFile)]
|
||||
self.file_to_file_index[&std::ptr::addr_of!(*source_file)]
|
||||
}
|
||||
|
||||
/// Encode something with additional information that allows to do some
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ pub enum SelectionCandidate<'tcx> {
|
|||
/// generated for an `async ||` expression.
|
||||
AsyncClosureCandidate,
|
||||
|
||||
/// Implementation of the the `AsyncFnKindHelper` helper trait, which
|
||||
/// Implementation of the `AsyncFnKindHelper` helper trait, which
|
||||
/// is used internally to delay computation for async closures until after
|
||||
/// upvar analysis is performed in HIR typeck.
|
||||
AsyncFnKindHelperCandidate,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ impl<T> List<T> {
|
|||
// length) that is 64-byte aligned, thus featuring the necessary
|
||||
// trailing padding for elements with up to 64-byte alignment.
|
||||
static EMPTY_SLICE: InOrder<usize, MaxAlign> = InOrder(0, MaxAlign);
|
||||
unsafe { &*(&EMPTY_SLICE as *const _ as *const List<T>) }
|
||||
unsafe { &*(std::ptr::addr_of!(EMPTY_SLICE) as *const List<T>) }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
|
|
|
|||
|
|
@ -840,12 +840,12 @@ pub struct OpaqueHiddenType<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> OpaqueHiddenType<'tcx> {
|
||||
pub fn report_mismatch(
|
||||
pub fn build_mismatch_error(
|
||||
&self,
|
||||
other: &Self,
|
||||
opaque_def_id: LocalDefId,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
) -> Result<DiagnosticBuilder<'tcx>, ErrorGuaranteed> {
|
||||
if let Some(diag) = tcx
|
||||
.sess
|
||||
.dcx()
|
||||
|
|
@ -853,18 +853,19 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
|
|||
{
|
||||
diag.cancel();
|
||||
}
|
||||
(self.ty, other.ty).error_reported()?;
|
||||
// Found different concrete types for the opaque type.
|
||||
let sub_diag = if self.span == other.span {
|
||||
TypeMismatchReason::ConflictType { span: self.span }
|
||||
} else {
|
||||
TypeMismatchReason::PreviousUse { span: self.span }
|
||||
};
|
||||
tcx.dcx().create_err(OpaqueHiddenTypeMismatch {
|
||||
Ok(tcx.dcx().create_err(OpaqueHiddenTypeMismatch {
|
||||
self_ty: self.ty,
|
||||
other_ty: other.ty,
|
||||
other_span: other.span,
|
||||
sub: sub_diag,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx), ret)]
|
||||
|
|
@ -1471,7 +1472,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
.filter(move |item| item.kind == AssocKind::Fn && item.defaultness(self).has_value())
|
||||
}
|
||||
|
||||
pub fn repr_options_of_def(self, did: DefId) -> ReprOptions {
|
||||
pub fn repr_options_of_def(self, did: LocalDefId) -> ReprOptions {
|
||||
let mut flags = ReprFlags::empty();
|
||||
let mut size = None;
|
||||
let mut max_align: Option<Align> = None;
|
||||
|
|
@ -1479,7 +1480,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
|
||||
// Generate a deterministically-derived seed from the item's path hash
|
||||
// to allow for cross-crate compilation to actually work
|
||||
let mut field_shuffle_seed = self.def_path_hash(did).0.to_smaller_hash().as_u64();
|
||||
let mut field_shuffle_seed =
|
||||
self.def_path_hash(did.to_def_id()).0.to_smaller_hash().as_u64();
|
||||
|
||||
// If the user defined a custom seed for layout randomization, xor the item's
|
||||
// path hash with the user defined seed, this will allowing determinism while
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ pub trait Printer<'tcx>: Sized {
|
|||
_,
|
||||
hir::CoroutineSource::Closure,
|
||||
)) = self.tcx().coroutine_kind(def_id)
|
||||
&& args.len() >= parent_args.len() + 1
|
||||
&& args.len() > parent_args.len()
|
||||
{
|
||||
return self.path_generic_args(
|
||||
|cx| cx.print_def_path(def_id, parent_args),
|
||||
|
|
|
|||
|
|
@ -2929,7 +2929,7 @@ define_print_and_forward_display! {
|
|||
|
||||
ty::ExistentialTraitRef<'tcx> {
|
||||
// Use a type that can't appear in defaults of type parameters.
|
||||
let dummy_self = Ty::new_fresh(cx.tcx(),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()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use rustc_data_structures::{
|
|||
};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::mir::{self, *};
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
|
@ -1052,18 +1052,31 @@ struct Ascription<'tcx> {
|
|||
variance: ty::Variance,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum TestCase<'pat, 'tcx> {
|
||||
Irrefutable { binding: Option<Binding<'tcx>>, ascription: Option<Ascription<'tcx>> },
|
||||
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
|
||||
Constant { value: mir::Const<'tcx> },
|
||||
Range(&'pat PatRange<'tcx>),
|
||||
Slice { len: usize, variable_length: bool },
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct MatchPair<'pat, 'tcx> {
|
||||
// This place...
|
||||
/// This place...
|
||||
place: PlaceBuilder<'tcx>,
|
||||
|
||||
// ... must match this pattern.
|
||||
// Invariant: after creation and simplification in `Candidate::new()`, all match pairs must be
|
||||
// simplified, i.e. require a test.
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
/// ... must pass this test...
|
||||
// Invariant: after creation and simplification in `Candidate::new()`, this must not be
|
||||
// `Irrefutable`.
|
||||
test_case: TestCase<'pat, 'tcx>,
|
||||
|
||||
/// Precomputed sub-match pairs of `pattern`.
|
||||
/// ... and these subpairs must match.
|
||||
subpairs: Vec<Self>,
|
||||
|
||||
/// The pattern this was created from.
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
}
|
||||
|
||||
/// See [`Test`] for more.
|
||||
|
|
@ -1137,39 +1150,61 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// the value, we will set and generate a branch to the appropriate
|
||||
/// pre-binding block.
|
||||
///
|
||||
/// If we find that *NONE* of the candidates apply, we branch to the
|
||||
/// `otherwise_block`, setting it to `Some` if required. In principle, this
|
||||
/// means that the input list was not exhaustive, though at present we
|
||||
/// sometimes are not smart enough to recognize all exhaustive inputs.
|
||||
/// If we find that *NONE* of the candidates apply, we branch to `otherwise_block`.
|
||||
///
|
||||
/// It might be surprising that the input can be non-exhaustive.
|
||||
/// Indeed, initially, it is not, because all matches are
|
||||
/// exhaustive in Rust. But during processing we sometimes divide
|
||||
/// up the list of candidates and recurse with a non-exhaustive
|
||||
/// list. This is important to keep the size of the generated code
|
||||
/// under control. See [`Builder::test_candidates`] for more details.
|
||||
/// list. This is how our lowering approach (called "backtracking
|
||||
/// automaton" in the literature) works.
|
||||
/// See [`Builder::test_candidates`] for more details.
|
||||
///
|
||||
/// If `fake_borrows` is `Some`, then places which need fake borrows
|
||||
/// will be added to it.
|
||||
///
|
||||
/// For an example of a case where we set `otherwise_block`, even for an
|
||||
/// exhaustive match, consider:
|
||||
///
|
||||
/// For an example of how we use `otherwise_block`, consider:
|
||||
/// ```
|
||||
/// # fn foo(x: (bool, bool)) {
|
||||
/// match x {
|
||||
/// (true, true) => (),
|
||||
/// (_, false) => (),
|
||||
/// (false, true) => (),
|
||||
/// # fn foo((x, y): (bool, bool)) -> u32 {
|
||||
/// match (x, y) {
|
||||
/// (true, true) => 1,
|
||||
/// (_, false) => 2,
|
||||
/// (false, true) => 3,
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// For this match, we generate something like:
|
||||
/// ```
|
||||
/// # fn foo((x, y): (bool, bool)) -> u32 {
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// return 1
|
||||
/// } else {
|
||||
/// // continue
|
||||
/// }
|
||||
/// } else {
|
||||
/// // continue
|
||||
/// }
|
||||
/// if y {
|
||||
/// if x {
|
||||
/// // This is actually unreachable because the `(true, true)` case was handled above.
|
||||
/// // continue
|
||||
/// } else {
|
||||
/// return 3
|
||||
/// }
|
||||
/// } else {
|
||||
/// return 2
|
||||
/// }
|
||||
/// // this is the final `otherwise_block`, which is unreachable because the match was exhaustive.
|
||||
/// unreachable!()
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// For this match, we check if `x.0` matches `true` (for the first
|
||||
/// arm). If it doesn't match, we check `x.1`. If `x.1` is `true` we check
|
||||
/// if `x.0` matches `false` (for the third arm). In the (impossible at
|
||||
/// runtime) case when `x.0` is now `true`, we branch to
|
||||
/// `otherwise_block`.
|
||||
/// Every `continue` is an instance of branching to some `otherwise_block` somewhere deep within
|
||||
/// the algorithm. For more details on why we lower like this, see [`Builder::test_candidates`].
|
||||
///
|
||||
/// Note how we test `x` twice. This is the tradeoff of backtracking automata: we prefer smaller
|
||||
/// code size at the expense of non-optimal code paths.
|
||||
#[instrument(skip(self, fake_borrows), level = "debug")]
|
||||
fn match_candidates<'pat>(
|
||||
&mut self,
|
||||
|
|
@ -1544,18 +1579,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// This is the most subtle part of the matching algorithm. At
|
||||
/// this point, the input candidates have been fully simplified,
|
||||
/// and so we know that all remaining match-pairs require some
|
||||
/// sort of test. To decide what test to perform, we take the highest
|
||||
/// priority candidate (the first one in the list, as of January 2021)
|
||||
/// and extract the first match-pair from the list. From this we decide
|
||||
/// what kind of test is needed using [`Builder::test`], defined in the
|
||||
/// [`test` module](mod@test).
|
||||
/// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at
|
||||
/// least one match pair. We currently simply pick the test corresponding to the first match
|
||||
/// pair of the first candidate in the list.
|
||||
///
|
||||
/// *Note:* taking the first match pair is somewhat arbitrary, and
|
||||
/// we might do better here by choosing more carefully what to
|
||||
/// test.
|
||||
/// *Note:* taking the first match pair is somewhat arbitrary, and we might do better here by
|
||||
/// choosing more carefully what to test.
|
||||
///
|
||||
/// For example, consider the following possible match-pairs:
|
||||
///
|
||||
|
|
@ -1567,121 +1596,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
/// [`Switch`]: TestKind::Switch
|
||||
/// [`SwitchInt`]: TestKind::SwitchInt
|
||||
/// [`Range`]: TestKind::Range
|
||||
///
|
||||
/// Once we know what sort of test we are going to perform, this
|
||||
/// test may also help us winnow down our candidates. So we walk over
|
||||
/// the candidates (from high to low priority) and check. This
|
||||
/// gives us, for each outcome of the test, a transformed list of
|
||||
/// candidates. For example, if we are testing `x.0`'s variant,
|
||||
/// and we have a candidate `(x.0 @ Some(v), x.1 @ 22)`,
|
||||
/// then we would have a resulting candidate of `((x.0 as Some).0 @ v, x.1 @ 22)`.
|
||||
/// Note that the first match-pair is now simpler (and, in fact, irrefutable).
|
||||
///
|
||||
/// But there may also be candidates that the test just doesn't
|
||||
/// apply to. The classical example involves wildcards:
|
||||
///
|
||||
/// ```
|
||||
/// # let (x, y, z) = (true, true, true);
|
||||
/// match (x, y, z) {
|
||||
/// (true , _ , true ) => true, // (0)
|
||||
/// (_ , true , _ ) => true, // (1)
|
||||
/// (false, false, _ ) => false, // (2)
|
||||
/// (true , _ , false) => false, // (3)
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// In that case, after we test on `x`, there are 2 overlapping candidate
|
||||
/// sets:
|
||||
///
|
||||
/// - If the outcome is that `x` is true, candidates 0, 1, and 3
|
||||
/// - If the outcome is that `x` is false, candidates 1 and 2
|
||||
///
|
||||
/// Here, the traditional "decision tree" method would generate 2
|
||||
/// separate code-paths for the 2 separate cases.
|
||||
///
|
||||
/// In some cases, this duplication can create an exponential amount of
|
||||
/// code. This is most easily seen by noticing that this method terminates
|
||||
/// with precisely the reachable arms being reachable - but that problem
|
||||
/// is trivially NP-complete:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match (var0, var1, var2, var3, ...) {
|
||||
/// (true , _ , _ , false, true, ...) => false,
|
||||
/// (_ , true, true , false, _ , ...) => false,
|
||||
/// (false, _ , false, false, _ , ...) => false,
|
||||
/// ...
|
||||
/// _ => true
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here the last arm is reachable only if there is an assignment to
|
||||
/// the variables that does not match any of the literals. Therefore,
|
||||
/// compilation would take an exponential amount of time in some cases.
|
||||
///
|
||||
/// That kind of exponential worst-case might not occur in practice, but
|
||||
/// our simplistic treatment of constants and guards would make it occur
|
||||
/// in very common situations - for example [#29740]:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match x {
|
||||
/// "foo" if foo_guard => ...,
|
||||
/// "bar" if bar_guard => ...,
|
||||
/// "baz" if baz_guard => ...,
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [#29740]: https://github.com/rust-lang/rust/issues/29740
|
||||
///
|
||||
/// Here we first test the match-pair `x @ "foo"`, which is an [`Eq` test].
|
||||
///
|
||||
/// [`Eq` test]: TestKind::Eq
|
||||
///
|
||||
/// It might seem that we would end up with 2 disjoint candidate
|
||||
/// sets, consisting of the first candidate or the other two, but our
|
||||
/// algorithm doesn't reason about `"foo"` being distinct from the other
|
||||
/// constants; it considers the latter arms to potentially match after
|
||||
/// both outcomes, which obviously leads to an exponential number
|
||||
/// of tests.
|
||||
///
|
||||
/// To avoid these kinds of problems, our algorithm tries to ensure
|
||||
/// the amount of generated tests is linear. When we do a k-way test,
|
||||
/// we return an additional "unmatched" set alongside the obvious `k`
|
||||
/// sets. When we encounter a candidate that would be present in more
|
||||
/// than one of the sets, we put it and all candidates below it into the
|
||||
/// "unmatched" set. This ensures these `k+1` sets are disjoint.
|
||||
///
|
||||
/// After we perform our test, we branch into the appropriate candidate
|
||||
/// set and recurse with `match_candidates`. These sub-matches are
|
||||
/// obviously non-exhaustive - as we discarded our otherwise set - so
|
||||
/// we set their continuation to do `match_candidates` on the
|
||||
/// "unmatched" set (which is again non-exhaustive).
|
||||
///
|
||||
/// If you apply this to the above test, you basically wind up
|
||||
/// with an if-else-if chain, testing each candidate in turn,
|
||||
/// which is precisely what we want.
|
||||
///
|
||||
/// In addition to avoiding exponential-time blowups, this algorithm
|
||||
/// also has the nice property that each guard and arm is only generated
|
||||
/// once.
|
||||
fn test_candidates<'pat, 'b, 'c>(
|
||||
fn pick_test(
|
||||
&mut self,
|
||||
span: Span,
|
||||
scrutinee_span: Span,
|
||||
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: BasicBlock,
|
||||
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
// extract the match-pair from the highest priority candidate
|
||||
) -> (PlaceBuilder<'tcx>, Test<'tcx>) {
|
||||
// Extract the match-pair from the highest priority candidate
|
||||
let match_pair = &candidates.first().unwrap().match_pairs[0];
|
||||
let mut test = self.test(match_pair);
|
||||
let match_place = match_pair.place.clone();
|
||||
|
||||
// most of the time, the test to perform is simply a function
|
||||
// of the main candidate; but for a test like SwitchInt, we
|
||||
// may want to add cases based on the candidates that are
|
||||
debug!(?test, ?match_pair);
|
||||
// Most of the time, the test to perform is simply a function of the main candidate; but for
|
||||
// a test like SwitchInt, we may want to add cases based on the candidates that are
|
||||
// available
|
||||
match test.kind {
|
||||
TestKind::SwitchInt { switch_ty: _, ref mut options } => {
|
||||
|
|
@ -1708,20 +1635,58 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fb.insert(resolved_place);
|
||||
}
|
||||
|
||||
// perform the test, branching to one of N blocks. For each of
|
||||
// those N possible outcomes, create a (initially empty)
|
||||
// vector of candidates. Those are the candidates that still
|
||||
// apply if the test has that particular outcome.
|
||||
debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
|
||||
(match_place, test)
|
||||
}
|
||||
|
||||
/// Given a test, we sort the input candidates into several buckets. If a candidate only matches
|
||||
/// in one of the branches of `test`, we move it there. If it could match in more than one of
|
||||
/// the branches of `test`, we stop sorting candidates.
|
||||
///
|
||||
/// This returns a pair of
|
||||
/// - the candidates that weren't sorted;
|
||||
/// - for each possible outcome of the test, the candidates that match in that outcome.
|
||||
///
|
||||
/// Moreover, we transform the branched candidates to reflect the fact that we know which
|
||||
/// outcome of `test` occurred.
|
||||
///
|
||||
/// For example:
|
||||
/// ```
|
||||
/// # let (x, y, z) = (true, true, true);
|
||||
/// match (x, y, z) {
|
||||
/// (true , _ , true ) => true, // (0)
|
||||
/// (false, false, _ ) => false, // (1)
|
||||
/// (_ , true , _ ) => true, // (2)
|
||||
/// (true , _ , false) => false, // (3)
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// Assume we are testing on `x`. There are 2 overlapping candidate sets:
|
||||
/// - If the outcome is that `x` is true, candidates 0, 2, and 3
|
||||
/// - If the outcome is that `x` is false, candidates 1 and 2
|
||||
///
|
||||
/// Following our algorithm, candidate 0 is sorted into outcome `x == true`, candidate 1 goes
|
||||
/// into outcome `x == false`, and candidate 2 and 3 remain unsorted.
|
||||
///
|
||||
/// The sorted candidates are transformed:
|
||||
/// - candidate 0 becomes `[z @ true]` since we know that `x` was `true`;
|
||||
/// - candidate 1 becomes `[y @ false]` since we know that `x` was `false`.
|
||||
fn sort_candidates<'b, 'c, 'pat>(
|
||||
&mut self,
|
||||
match_place: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
) -> (&'b mut [&'c mut Candidate<'pat, 'tcx>], Vec<Vec<&'b mut Candidate<'pat, 'tcx>>>) {
|
||||
// For each of the N possible outcomes, create a (initially empty) vector of candidates.
|
||||
// Those are the candidates that apply if the test has that particular outcome.
|
||||
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
|
||||
target_candidates.resize_with(test.targets(), Default::default);
|
||||
|
||||
let total_candidate_count = candidates.len();
|
||||
|
||||
// Sort the candidates into the appropriate vector in
|
||||
// `target_candidates`. Note that at some point we may
|
||||
// encounter a candidate where the test is not relevant; at
|
||||
// that point, we stop sorting.
|
||||
// Sort the candidates into the appropriate vector in `target_candidates`. Note that at some
|
||||
// point we may encounter a candidate where the test is not relevant; at that point, we stop
|
||||
// sorting.
|
||||
while let Some(candidate) = candidates.first_mut() {
|
||||
let Some(idx) = self.sort_candidate(&match_place, &test, candidate) else {
|
||||
break;
|
||||
|
|
@ -1730,7 +1695,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
target_candidates[idx].push(candidate);
|
||||
candidates = rest;
|
||||
}
|
||||
// at least the first candidate ought to be tested
|
||||
|
||||
// At least the first candidate ought to be tested
|
||||
assert!(
|
||||
total_candidate_count > candidates.len(),
|
||||
"{total_candidate_count}, {candidates:#?}"
|
||||
|
|
@ -1738,16 +1704,130 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("untested_candidates: {}", candidates.len());
|
||||
|
||||
(candidates, target_candidates)
|
||||
}
|
||||
|
||||
/// This is the most subtle part of the match lowering algorithm. At this point, the input
|
||||
/// candidates have been fully simplified, so all remaining match-pairs require some sort of
|
||||
/// test.
|
||||
///
|
||||
/// Once we pick what sort of test we are going to perform, this test will help us winnow down
|
||||
/// our candidates. So we walk over the candidates (from high to low priority) and check. We
|
||||
/// compute, for each outcome of the test, a transformed list of candidates. If a candidate
|
||||
/// matches in a single branch of our test, we add it to the corresponding outcome. We also
|
||||
/// transform it to record the fact that we know which outcome occurred.
|
||||
///
|
||||
/// For example, if we are testing `x.0`'s variant, and we have a candidate `(x.0 @ Some(v), x.1
|
||||
/// @ 22)`, then we would have a resulting candidate of `((x.0 as Some).0 @ v, x.1 @ 22)` in the
|
||||
/// branch corresponding to `Some`. To ensure we make progress, we always pick a test that
|
||||
/// results in simplifying the first candidate.
|
||||
///
|
||||
/// But there may also be candidates that the test doesn't
|
||||
/// apply to. The classical example is wildcards:
|
||||
///
|
||||
/// ```
|
||||
/// # let (x, y, z) = (true, true, true);
|
||||
/// match (x, y, z) {
|
||||
/// (true , _ , true ) => true, // (0)
|
||||
/// (false, false, _ ) => false, // (1)
|
||||
/// (_ , true , _ ) => true, // (2)
|
||||
/// (true , _ , false) => false, // (3)
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// Here, the traditional "decision tree" method would generate 2 separate code-paths for the 2
|
||||
/// possible values of `x`. This would however duplicate some candidates, which would need to be
|
||||
/// lowered several times.
|
||||
///
|
||||
/// In some cases, this duplication can create an exponential amount of
|
||||
/// code. This is most easily seen by noticing that this method terminates
|
||||
/// with precisely the reachable arms being reachable - but that problem
|
||||
/// is trivially NP-complete:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// match (var0, var1, var2, var3, ...) {
|
||||
/// (true , _ , _ , false, true, ...) => false,
|
||||
/// (_ , true, true , false, _ , ...) => false,
|
||||
/// (false, _ , false, false, _ , ...) => false,
|
||||
/// ...
|
||||
/// _ => true
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Here the last arm is reachable only if there is an assignment to
|
||||
/// the variables that does not match any of the literals. Therefore,
|
||||
/// compilation would take an exponential amount of time in some cases.
|
||||
///
|
||||
/// In rustc, we opt instead for the "backtracking automaton" approach. This guarantees we never
|
||||
/// duplicate a candidate (except in the presence of or-patterns). In fact this guarantee is
|
||||
/// ensured by the fact that we carry around `&mut Candidate`s which can't be duplicated.
|
||||
///
|
||||
/// To make this work, whenever we decide to perform a test, if we encounter a candidate that
|
||||
/// could match in more than one branch of the test, we stop. We generate code for the test and
|
||||
/// for the candidates in its branches; the remaining candidates will be tested if the
|
||||
/// candidates in the branches fail to match.
|
||||
///
|
||||
/// For example, if we test on `x` in the following:
|
||||
/// ```
|
||||
/// # fn foo((x, y, z): (bool, bool, bool)) -> u32 {
|
||||
/// match (x, y, z) {
|
||||
/// (true , _ , true ) => 0,
|
||||
/// (false, false, _ ) => 1,
|
||||
/// (_ , true , _ ) => 2,
|
||||
/// (true , _ , false) => 3,
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
/// this function generates code that looks more of less like:
|
||||
/// ```
|
||||
/// # fn foo((x, y, z): (bool, bool, bool)) -> u32 {
|
||||
/// if x {
|
||||
/// match (y, z) {
|
||||
/// (_, true) => return 0,
|
||||
/// _ => {} // continue matching
|
||||
/// }
|
||||
/// } else {
|
||||
/// match (y, z) {
|
||||
/// (false, _) => return 1,
|
||||
/// _ => {} // continue matching
|
||||
/// }
|
||||
/// }
|
||||
/// // the block here is `remainder_start`
|
||||
/// match (x, y, z) {
|
||||
/// (_ , true , _ ) => 2,
|
||||
/// (true , _ , false) => 3,
|
||||
/// _ => unreachable!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
fn test_candidates<'pat, 'b, 'c>(
|
||||
&mut self,
|
||||
span: Span,
|
||||
scrutinee_span: Span,
|
||||
candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: BasicBlock,
|
||||
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
|
||||
) {
|
||||
// Extract the match-pair from the highest priority candidate and build a test from it.
|
||||
let (match_place, test) = self.pick_test(candidates, fake_borrows);
|
||||
|
||||
// For each of the N possible test outcomes, build the vector of candidates that applies if
|
||||
// the test has that particular outcome.
|
||||
let (remaining_candidates, target_candidates) =
|
||||
self.sort_candidates(&match_place, &test, candidates);
|
||||
|
||||
// The block that we should branch to if none of the
|
||||
// `target_candidates` match.
|
||||
let remainder_start = if !candidates.is_empty() {
|
||||
let remainder_start = if !remaining_candidates.is_empty() {
|
||||
let remainder_start = self.cfg.start_new_block();
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
remainder_start,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
remaining_candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
remainder_start
|
||||
|
|
|
|||
|
|
@ -13,11 +13,9 @@
|
|||
//! testing a value against a constant.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
|
||||
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair, TestCase};
|
||||
use crate::build::Builder;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::thir::{Pat, PatKind};
|
||||
|
||||
use std::mem;
|
||||
|
||||
|
|
@ -62,13 +60,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let mut simplified_match_pairs = Vec::new();
|
||||
// Repeatedly simplify match pairs until we're left with only unsimplifiable ones.
|
||||
loop {
|
||||
for match_pair in mem::take(match_pairs) {
|
||||
if let Err(match_pair) = self.simplify_match_pair(
|
||||
match_pair,
|
||||
candidate_bindings,
|
||||
candidate_ascriptions,
|
||||
match_pairs,
|
||||
) {
|
||||
for mut match_pair in mem::take(match_pairs) {
|
||||
if let TestCase::Irrefutable { binding, ascription } = match_pair.test_case {
|
||||
if let Some(binding) = binding {
|
||||
candidate_bindings.push(binding);
|
||||
}
|
||||
if let Some(ascription) = ascription {
|
||||
candidate_ascriptions.push(ascription);
|
||||
}
|
||||
// Simplifiable pattern; we replace it with its subpairs and simplify further.
|
||||
match_pairs.append(&mut match_pair.subpairs);
|
||||
} else {
|
||||
// Unsimplifiable pattern; we recursively simplify its subpairs and don't
|
||||
// process it further.
|
||||
self.simplify_match_pairs(
|
||||
&mut match_pair.subpairs,
|
||||
candidate_bindings,
|
||||
candidate_ascriptions,
|
||||
);
|
||||
simplified_match_pairs.push(match_pair);
|
||||
}
|
||||
}
|
||||
|
|
@ -117,133 +126,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Tries to simplify `match_pair`, returning `Ok(())` if successful. If successful, new match
|
||||
/// pairs and bindings will have been pushed into the respective `Vec`s. If no simplification is
|
||||
/// possible, `Err` is returned.
|
||||
fn simplify_match_pair<'pat>(
|
||||
&mut self,
|
||||
mut match_pair: MatchPair<'pat, 'tcx>,
|
||||
bindings: &mut Vec<Binding<'tcx>>,
|
||||
ascriptions: &mut Vec<Ascription<'tcx>>,
|
||||
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
|
||||
) -> Result<(), MatchPair<'pat, 'tcx>> {
|
||||
match match_pair.pattern.kind {
|
||||
PatKind::Leaf { .. }
|
||||
| PatKind::Deref { .. }
|
||||
| PatKind::Array { .. }
|
||||
| PatKind::Never
|
||||
| PatKind::Wild
|
||||
| PatKind::Error(_) => {}
|
||||
|
||||
PatKind::AscribeUserType {
|
||||
ascription: thir::Ascription { ref annotation, variance },
|
||||
..
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`
|
||||
if let Some(source) = match_pair.place.try_to_place(self) {
|
||||
ascriptions.push(Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source,
|
||||
variance,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Binding {
|
||||
name: _,
|
||||
mutability: _,
|
||||
mode,
|
||||
var,
|
||||
ty: _,
|
||||
subpattern: _,
|
||||
is_primary: _,
|
||||
} => {
|
||||
if let Some(source) = match_pair.place.try_to_place(self) {
|
||||
bindings.push(Binding {
|
||||
span: match_pair.pattern.span,
|
||||
source,
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::InlineConstant { subpattern: ref pattern, def } => {
|
||||
// Apply a type ascription for the inline constant to the value at `match_pair.place`
|
||||
if let Some(source) = match_pair.place.try_to_place(self) {
|
||||
let span = match_pair.pattern.span;
|
||||
let parent_id = self.tcx.typeck_root_def_id(self.def_id.to_def_id());
|
||||
let args = ty::InlineConstArgs::new(
|
||||
self.tcx,
|
||||
ty::InlineConstArgsParts {
|
||||
parent_args: ty::GenericArgs::identity_for_item(self.tcx, parent_id),
|
||||
ty: self.infcx.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span,
|
||||
}),
|
||||
},
|
||||
)
|
||||
.args;
|
||||
let user_ty =
|
||||
self.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
|
||||
def.to_def_id(),
|
||||
ty::UserArgs { args, user_self_ty: None },
|
||||
));
|
||||
let annotation = ty::CanonicalUserTypeAnnotation {
|
||||
inferred_ty: pattern.ty,
|
||||
span,
|
||||
user_ty: Box::new(user_ty),
|
||||
};
|
||||
ascriptions.push(Ascription {
|
||||
annotation,
|
||||
source,
|
||||
variance: ty::Contravariant,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Constant { .. } => {
|
||||
// FIXME normalize patterns when possible
|
||||
return Err(match_pair);
|
||||
}
|
||||
|
||||
PatKind::Range(ref range) => {
|
||||
if range.is_full_range(self.tcx) != Some(true) {
|
||||
return Err(match_pair);
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
if !(prefix.is_empty() && slice.is_some() && suffix.is_empty()) {
|
||||
self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
|
||||
return Err(match_pair);
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Variant { adt_def, args, variant_index, subpatterns: _ } => {
|
||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||
i == variant_index || {
|
||||
(self.tcx.features().exhaustive_patterns
|
||||
|| self.tcx.features().min_exhaustive_patterns)
|
||||
&& !v
|
||||
.inhabited_predicate(self.tcx, adt_def)
|
||||
.instantiate(self.tcx, args)
|
||||
.apply_ignore_module(self.tcx, self.param_env)
|
||||
}
|
||||
}) && (adt_def.did().is_local()
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if !irrefutable {
|
||||
self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
|
||||
return Err(match_pair);
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Or { .. } => return Err(match_pair),
|
||||
}
|
||||
|
||||
// Simplifiable pattern; we replace it with its subpairs.
|
||||
match_pairs.append(&mut match_pair.subpairs);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
// the candidates based on the result.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestCase, TestKind};
|
||||
use crate::build::Builder;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::{LangItem, RangeEnd};
|
||||
|
|
@ -29,58 +29,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
///
|
||||
/// It is a bug to call this with a not-fully-simplified pattern.
|
||||
pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
|
||||
match match_pair.pattern.kind {
|
||||
PatKind::Variant { adt_def, args: _, variant_index: _, subpatterns: _ } => Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::Switch {
|
||||
adt_def,
|
||||
variants: BitSet::new_empty(adt_def.variants().len()),
|
||||
},
|
||||
},
|
||||
let kind = match match_pair.test_case {
|
||||
TestCase::Variant { adt_def, variant_index: _ } => {
|
||||
TestKind::Switch { adt_def, variants: BitSet::new_empty(adt_def.variants().len()) }
|
||||
}
|
||||
|
||||
PatKind::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
|
||||
TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
|
||||
// For integers, we use a `SwitchInt` match, which allows
|
||||
// us to handle more cases.
|
||||
Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::SwitchInt {
|
||||
switch_ty: match_pair.pattern.ty,
|
||||
TestKind::SwitchInt {
|
||||
switch_ty: match_pair.pattern.ty,
|
||||
|
||||
// these maps are empty to start; cases are
|
||||
// added below in add_cases_to_switch
|
||||
options: Default::default(),
|
||||
},
|
||||
// these maps are empty to start; cases are
|
||||
// added below in add_cases_to_switch
|
||||
options: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Constant { value } => Test {
|
||||
span: match_pair.pattern.span,
|
||||
kind: TestKind::Eq { value, ty: match_pair.pattern.ty },
|
||||
},
|
||||
TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern.ty },
|
||||
|
||||
PatKind::Range(ref range) => {
|
||||
TestCase::Range(range) => {
|
||||
assert_eq!(range.ty, match_pair.pattern.ty);
|
||||
Test { span: match_pair.pattern.span, kind: TestKind::Range(range.clone()) }
|
||||
TestKind::Range(Box::new(range.clone()))
|
||||
}
|
||||
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
let len = prefix.len() + suffix.len();
|
||||
let op = if slice.is_some() { BinOp::Ge } else { BinOp::Eq };
|
||||
Test { span: match_pair.pattern.span, kind: TestKind::Len { len: len as u64, op } }
|
||||
TestCase::Slice { len, variable_length } => {
|
||||
let op = if variable_length { BinOp::Ge } else { BinOp::Eq };
|
||||
TestKind::Len { len: len as u64, op }
|
||||
}
|
||||
|
||||
PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
|
||||
TestCase::Or { .. } => bug!("or-patterns should have already been handled"),
|
||||
|
||||
PatKind::AscribeUserType { .. }
|
||||
| PatKind::InlineConstant { .. }
|
||||
| PatKind::Array { .. }
|
||||
| PatKind::Wild
|
||||
| PatKind::Binding { .. }
|
||||
| PatKind::Never
|
||||
| PatKind::Leaf { .. }
|
||||
| PatKind::Deref { .. }
|
||||
| PatKind::Error(_) => self.error_simplifiable(match_pair),
|
||||
}
|
||||
TestCase::Irrefutable { .. } => span_bug!(
|
||||
match_pair.pattern.span,
|
||||
"simplifiable pattern found: {:?}",
|
||||
match_pair.pattern
|
||||
),
|
||||
};
|
||||
|
||||
Test { span: match_pair.pattern.span, kind }
|
||||
}
|
||||
|
||||
pub(super) fn add_cases_to_switch<'pat>(
|
||||
|
|
@ -94,32 +81,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
return false;
|
||||
};
|
||||
|
||||
match match_pair.pattern.kind {
|
||||
PatKind::Constant { value } => {
|
||||
match match_pair.test_case {
|
||||
TestCase::Constant { value } => {
|
||||
options.entry(value).or_insert_with(|| value.eval_bits(self.tcx, self.param_env));
|
||||
true
|
||||
}
|
||||
PatKind::Variant { .. } => {
|
||||
TestCase::Variant { .. } => {
|
||||
panic!("you should have called add_variants_to_switch instead!");
|
||||
}
|
||||
PatKind::Range(ref range) => {
|
||||
TestCase::Range(ref range) => {
|
||||
// Check that none of the switch values are in the range.
|
||||
self.values_not_contained_in_range(&*range, options).unwrap_or(false)
|
||||
}
|
||||
PatKind::Slice { .. }
|
||||
| PatKind::Array { .. }
|
||||
| PatKind::Wild
|
||||
| PatKind::Never
|
||||
| PatKind::Or { .. }
|
||||
| PatKind::Binding { .. }
|
||||
| PatKind::AscribeUserType { .. }
|
||||
| PatKind::InlineConstant { .. }
|
||||
| PatKind::Leaf { .. }
|
||||
| PatKind::Deref { .. }
|
||||
| PatKind::Error(_) => {
|
||||
// don't know how to add these patterns to a switch
|
||||
false
|
||||
}
|
||||
// don't know how to add these patterns to a switch
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,17 +109,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
return false;
|
||||
};
|
||||
|
||||
match match_pair.pattern.kind {
|
||||
PatKind::Variant { adt_def: _, variant_index, .. } => {
|
||||
match match_pair.test_case {
|
||||
TestCase::Variant { variant_index, .. } => {
|
||||
// We have a pattern testing for variant `variant_index`
|
||||
// set the corresponding index to true
|
||||
variants.insert(variant_index);
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
// don't know how to add these patterns to a switch
|
||||
false
|
||||
}
|
||||
// don't know how to add these patterns to a switch
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -591,12 +564,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?;
|
||||
|
||||
let fully_matched;
|
||||
let ret = match (&test.kind, &match_pair.pattern.kind) {
|
||||
let ret = match (&test.kind, &match_pair.test_case) {
|
||||
// If we are performing a variant switch, then this
|
||||
// informs variant patterns, but nothing else.
|
||||
(
|
||||
&TestKind::Switch { adt_def: tested_adt_def, .. },
|
||||
&PatKind::Variant { adt_def, variant_index, .. },
|
||||
&TestCase::Variant { adt_def, variant_index },
|
||||
) => {
|
||||
assert_eq!(adt_def, tested_adt_def);
|
||||
fully_matched = true;
|
||||
|
|
@ -612,14 +585,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
//
|
||||
// FIXME(#29623) we could use PatKind::Range to rule
|
||||
// things out here, in some cases.
|
||||
(TestKind::SwitchInt { switch_ty: _, options }, PatKind::Constant { value })
|
||||
(TestKind::SwitchInt { switch_ty: _, options }, TestCase::Constant { value })
|
||||
if is_switch_ty(match_pair.pattern.ty) =>
|
||||
{
|
||||
fully_matched = true;
|
||||
let index = options.get_index_of(value).unwrap();
|
||||
Some(index)
|
||||
}
|
||||
(TestKind::SwitchInt { switch_ty: _, options }, PatKind::Range(range)) => {
|
||||
(TestKind::SwitchInt { switch_ty: _, options }, TestCase::Range(range)) => {
|
||||
fully_matched = false;
|
||||
let not_contained =
|
||||
self.values_not_contained_in_range(&*range, options).unwrap_or(false);
|
||||
|
|
@ -637,11 +610,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
(
|
||||
&TestKind::Len { len: test_len, op: BinOp::Eq },
|
||||
PatKind::Slice { prefix, slice, suffix },
|
||||
&TestCase::Slice { len, variable_length },
|
||||
) => {
|
||||
let pat_len = (prefix.len() + suffix.len()) as u64;
|
||||
match (test_len.cmp(&pat_len), slice) {
|
||||
(Ordering::Equal, &None) => {
|
||||
match (test_len.cmp(&(len as u64)), variable_length) {
|
||||
(Ordering::Equal, false) => {
|
||||
// on true, min_len = len = $actual_length,
|
||||
// on false, len != $actual_length
|
||||
fully_matched = true;
|
||||
|
|
@ -654,13 +626,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fully_matched = false;
|
||||
Some(1)
|
||||
}
|
||||
(Ordering::Equal | Ordering::Greater, &Some(_)) => {
|
||||
(Ordering::Equal | Ordering::Greater, true) => {
|
||||
// This can match both if $actual_len = test_len >= pat_len,
|
||||
// and if $actual_len > test_len. We can't advance.
|
||||
fully_matched = false;
|
||||
None
|
||||
}
|
||||
(Ordering::Greater, &None) => {
|
||||
(Ordering::Greater, false) => {
|
||||
// test_len != pat_len, so if $actual_len = test_len, then
|
||||
// $actual_len != pat_len.
|
||||
fully_matched = false;
|
||||
|
|
@ -670,31 +642,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
(
|
||||
&TestKind::Len { len: test_len, op: BinOp::Ge },
|
||||
PatKind::Slice { prefix, slice, suffix },
|
||||
&TestCase::Slice { len, variable_length },
|
||||
) => {
|
||||
// the test is `$actual_len >= test_len`
|
||||
let pat_len = (prefix.len() + suffix.len()) as u64;
|
||||
match (test_len.cmp(&pat_len), slice) {
|
||||
(Ordering::Equal, &Some(_)) => {
|
||||
match (test_len.cmp(&(len as u64)), variable_length) {
|
||||
(Ordering::Equal, true) => {
|
||||
// $actual_len >= test_len = pat_len,
|
||||
// so we can match.
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
}
|
||||
(Ordering::Less, _) | (Ordering::Equal, &None) => {
|
||||
(Ordering::Less, _) | (Ordering::Equal, false) => {
|
||||
// test_len <= pat_len. If $actual_len < test_len,
|
||||
// then it is also < pat_len, so the test passing is
|
||||
// necessary (but insufficient).
|
||||
fully_matched = false;
|
||||
Some(0)
|
||||
}
|
||||
(Ordering::Greater, &None) => {
|
||||
(Ordering::Greater, false) => {
|
||||
// test_len > pat_len. If $actual_len >= test_len > pat_len,
|
||||
// then we know we won't have a match.
|
||||
fully_matched = false;
|
||||
Some(1)
|
||||
}
|
||||
(Ordering::Greater, &Some(_)) => {
|
||||
(Ordering::Greater, true) => {
|
||||
// test_len < pat_len, and is therefore less
|
||||
// strict. This can still go both ways.
|
||||
fully_matched = false;
|
||||
|
|
@ -703,8 +674,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
(TestKind::Range(test), PatKind::Range(pat)) => {
|
||||
if test == pat {
|
||||
(TestKind::Range(test), &TestCase::Range(pat)) => {
|
||||
if test.as_ref() == pat {
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
} else {
|
||||
|
|
@ -714,7 +685,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
|
||||
}
|
||||
}
|
||||
(TestKind::Range(range), &PatKind::Constant { value }) => {
|
||||
(TestKind::Range(range), &TestCase::Constant { value }) => {
|
||||
fully_matched = false;
|
||||
if !range.contains(value, self.tcx, self.param_env)? {
|
||||
// `value` is not contained in the testing range,
|
||||
|
|
@ -737,7 +708,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// However, at this point we can still encounter or-patterns that were extracted
|
||||
// from previous calls to `sort_candidate`, so we need to manually address that
|
||||
// case to avoid panicking in `self.test()`.
|
||||
if let PatKind::Or { .. } = &match_pair.pattern.kind {
|
||||
if let TestCase::Or { .. } = &match_pair.test_case {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -760,18 +731,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let match_pair = candidate.match_pairs.remove(match_pair_index);
|
||||
candidate.match_pairs.extend(match_pair.subpairs);
|
||||
// Move or-patterns to the end.
|
||||
candidate
|
||||
.match_pairs
|
||||
.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
|
||||
candidate.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn error_simplifiable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
|
||||
span_bug!(match_pair.pattern.span, "simplifiable pattern found: {:?}", match_pair.pattern)
|
||||
}
|
||||
|
||||
fn values_not_contained_in_range(
|
||||
&self,
|
||||
range: &PatRange<'tcx>,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::build::expr::as_place::PlaceBase;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::MatchPair;
|
||||
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||
use crate::build::matches::{MatchPair, TestCase};
|
||||
use crate::build::Builder;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
|
||||
|
|
@ -117,50 +117,144 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
|||
place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
|
||||
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
|
||||
let mut subpairs = Vec::new();
|
||||
match pattern.kind {
|
||||
PatKind::Constant { .. }
|
||||
| PatKind::Range(_)
|
||||
| PatKind::Or { .. }
|
||||
| PatKind::Never
|
||||
| PatKind::Wild
|
||||
| PatKind::Error(_) => {}
|
||||
let test_case = match pattern.kind {
|
||||
PatKind::Never | PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
|
||||
PatKind::Or { .. } => TestCase::Or,
|
||||
|
||||
PatKind::AscribeUserType { ref subpattern, .. } => {
|
||||
subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
|
||||
PatKind::Range(ref range) => {
|
||||
if range.is_full_range(cx.tcx) == Some(true) {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Range(range)
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Binding { ref subpattern, .. } => {
|
||||
PatKind::Constant { value } => TestCase::Constant { value },
|
||||
|
||||
PatKind::AscribeUserType {
|
||||
ascription: thir::Ascription { ref annotation, variance },
|
||||
ref subpattern,
|
||||
..
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`
|
||||
let ascription = place.try_to_place(cx).map(|source| super::Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source,
|
||||
variance,
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Binding {
|
||||
name: _,
|
||||
mutability: _,
|
||||
mode,
|
||||
var,
|
||||
ty: _,
|
||||
ref subpattern,
|
||||
is_primary: _,
|
||||
} => {
|
||||
let binding = place.try_to_place(cx).map(|source| super::Binding {
|
||||
span: pattern.span,
|
||||
source,
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
|
||||
if let Some(subpattern) = subpattern.as_ref() {
|
||||
// this is the `x @ P` case; have to keep matching against `P` now
|
||||
subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
|
||||
}
|
||||
TestCase::Irrefutable { ascription: None, binding }
|
||||
}
|
||||
|
||||
PatKind::InlineConstant { subpattern: ref pattern, .. } => {
|
||||
PatKind::InlineConstant { subpattern: ref pattern, def, .. } => {
|
||||
// Apply a type ascription for the inline constant to the value at `match_pair.place`
|
||||
let ascription = place.try_to_place(cx).map(|source| {
|
||||
let span = pattern.span;
|
||||
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
|
||||
let args = ty::InlineConstArgs::new(
|
||||
cx.tcx,
|
||||
ty::InlineConstArgsParts {
|
||||
parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
|
||||
ty: cx.infcx.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span,
|
||||
}),
|
||||
},
|
||||
)
|
||||
.args;
|
||||
let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
|
||||
def.to_def_id(),
|
||||
ty::UserArgs { args, user_self_ty: None },
|
||||
));
|
||||
let annotation = ty::CanonicalUserTypeAnnotation {
|
||||
inferred_ty: pattern.ty,
|
||||
span,
|
||||
user_ty: Box::new(user_ty),
|
||||
};
|
||||
super::Ascription { annotation, source, variance: ty::Contravariant }
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place.clone(), pattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix }
|
||||
| PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix);
|
||||
default_irrefutable()
|
||||
}
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix);
|
||||
|
||||
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Slice {
|
||||
len: prefix.len() + suffix.len(),
|
||||
variable_length: slice.is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
|
||||
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
|
||||
let downcast_place = place.clone().downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
|
||||
|
||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||
i == variant_index || {
|
||||
(cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& !v
|
||||
.inhabited_predicate(cx.tcx, adt_def)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply_ignore_module(cx.tcx, cx.param_env)
|
||||
}
|
||||
}) && (adt_def.did().is_local()
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if irrefutable {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Variant { adt_def, variant_index }
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Leaf { ref subpatterns } => {
|
||||
subpairs = cx.field_match_pairs(place.clone(), subpatterns);
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
let place_builder = place.clone().deref();
|
||||
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
|
||||
default_irrefutable()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MatchPair { place, pattern, subpairs }
|
||||
MatchPair { place, test_case, subpairs, pattern }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -461,8 +461,10 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
|
|||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'_> {
|
||||
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G>
|
||||
for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_>
|
||||
{
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
|
||||
let mut diag = DiagnosticBuilder::new(
|
||||
dcx,
|
||||
level,
|
||||
|
|
|
|||
|
|
@ -223,19 +223,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
// If we are handling a range with associated constants (e.g.
|
||||
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
|
||||
// constants somewhere. Have them on the range pattern.
|
||||
for ascr in [lo_ascr, hi_ascr] {
|
||||
if let Some(ascription) = ascr {
|
||||
kind = PatKind::AscribeUserType {
|
||||
ascription,
|
||||
subpattern: Box::new(Pat { span, ty, kind }),
|
||||
};
|
||||
}
|
||||
for ascription in [lo_ascr, hi_ascr].into_iter().flatten() {
|
||||
kind = PatKind::AscribeUserType {
|
||||
ascription,
|
||||
subpattern: Box::new(Pat { span, ty, kind }),
|
||||
};
|
||||
}
|
||||
for inline_const in [lo_inline, hi_inline] {
|
||||
if let Some(def) = inline_const {
|
||||
kind =
|
||||
PatKind::InlineConstant { def, subpattern: Box::new(Pat { span, ty, kind }) };
|
||||
}
|
||||
for def in [lo_inline, hi_inline].into_iter().flatten() {
|
||||
kind = PatKind::InlineConstant { def, subpattern: Box::new(Pat { span, ty, kind }) };
|
||||
}
|
||||
Ok(kind)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,19 +52,18 @@ impl CoverageGraph {
|
|||
}
|
||||
}
|
||||
|
||||
let mut basic_coverage_blocks =
|
||||
Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
|
||||
let dominators = dominators::dominators(&basic_coverage_blocks);
|
||||
basic_coverage_blocks.dominators = Some(dominators);
|
||||
let mut this = Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
|
||||
|
||||
this.dominators = Some(dominators::dominators(&this));
|
||||
|
||||
// The coverage graph's entry-point node (bcb0) always starts with bb0,
|
||||
// which never has predecessors. Any other blocks merged into bcb0 can't
|
||||
// have multiple (coverage-relevant) predecessors, so bcb0 always has
|
||||
// zero in-edges.
|
||||
assert!(basic_coverage_blocks[START_BCB].leader_bb() == mir::START_BLOCK);
|
||||
assert!(basic_coverage_blocks.predecessors[START_BCB].is_empty());
|
||||
assert!(this[START_BCB].leader_bb() == mir::START_BLOCK);
|
||||
assert!(this.predecessors[START_BCB].is_empty());
|
||||
|
||||
basic_coverage_blocks
|
||||
this
|
||||
}
|
||||
|
||||
fn compute_basic_coverage_blocks(
|
||||
|
|
|
|||
|
|
@ -90,23 +90,23 @@ pub(super) fn generate_coverage_spans(
|
|||
struct CurrCovspan {
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
is_hole: bool,
|
||||
}
|
||||
|
||||
impl CurrCovspan {
|
||||
fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
|
||||
Self { span, bcb, is_closure }
|
||||
fn new(span: Span, bcb: BasicCoverageBlock, is_hole: bool) -> Self {
|
||||
Self { span, bcb, is_hole }
|
||||
}
|
||||
|
||||
fn into_prev(self) -> PrevCovspan {
|
||||
let Self { span, bcb, is_closure } = self;
|
||||
PrevCovspan { span, bcb, merged_spans: vec![span], is_closure }
|
||||
let Self { span, bcb, is_hole } = self;
|
||||
PrevCovspan { span, bcb, merged_spans: vec![span], is_hole }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
// This is only called in cases where `curr` is a closure span that has
|
||||
// This is only called in cases where `curr` is a hole span that has
|
||||
// been carved out of `prev`.
|
||||
debug_assert!(self.is_closure);
|
||||
debug_assert!(self.is_hole);
|
||||
self.into_prev().into_refined()
|
||||
}
|
||||
}
|
||||
|
|
@ -118,12 +118,12 @@ struct PrevCovspan {
|
|||
/// List of all the original spans from MIR that have been merged into this
|
||||
/// span. Mainly used to precisely skip over gaps when truncating a span.
|
||||
merged_spans: Vec<Span>,
|
||||
is_closure: bool,
|
||||
is_hole: bool,
|
||||
}
|
||||
|
||||
impl PrevCovspan {
|
||||
fn is_mergeable(&self, other: &CurrCovspan) -> bool {
|
||||
self.bcb == other.bcb && !self.is_closure && !other.is_closure
|
||||
self.bcb == other.bcb && !self.is_hole && !other.is_hole
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, other: &CurrCovspan) {
|
||||
|
|
@ -142,8 +142,8 @@ impl PrevCovspan {
|
|||
}
|
||||
|
||||
fn refined_copy(&self) -> RefinedCovspan {
|
||||
let &Self { span, bcb, merged_spans: _, is_closure } = self;
|
||||
RefinedCovspan { span, bcb, is_closure }
|
||||
let &Self { span, bcb, merged_spans: _, is_hole } = self;
|
||||
RefinedCovspan { span, bcb, is_hole }
|
||||
}
|
||||
|
||||
fn into_refined(self) -> RefinedCovspan {
|
||||
|
|
@ -156,12 +156,12 @@ impl PrevCovspan {
|
|||
struct RefinedCovspan {
|
||||
span: Span,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
is_hole: bool,
|
||||
}
|
||||
|
||||
impl RefinedCovspan {
|
||||
fn is_mergeable(&self, other: &Self) -> bool {
|
||||
self.bcb == other.bcb && !self.is_closure && !other.is_closure
|
||||
self.bcb == other.bcb && !self.is_hole && !other.is_hole
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, other: &Self) {
|
||||
|
|
@ -176,16 +176,16 @@ impl RefinedCovspan {
|
|||
/// * Remove duplicate source code coverage regions
|
||||
/// * Merge spans that represent continuous (both in source code and control flow), non-branching
|
||||
/// execution
|
||||
/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
|
||||
/// * Carve out (leave uncovered) any "hole" spans that need to be left blank
|
||||
/// (e.g. closures that will be counted by their own MIR body)
|
||||
struct SpansRefiner {
|
||||
/// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
|
||||
/// dominance between the `BasicCoverageBlock`s of equal `Span`s.
|
||||
sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
|
||||
|
||||
/// The current coverage span to compare to its `prev`, to possibly merge, discard, force the
|
||||
/// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to
|
||||
/// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next
|
||||
/// iteration.
|
||||
/// The current coverage span to compare to its `prev`, to possibly merge, discard,
|
||||
/// or cause `prev` to be modified or discarded.
|
||||
/// If `curr` is not discarded or merged, it becomes `prev` for the next iteration.
|
||||
some_curr: Option<CurrCovspan>,
|
||||
|
||||
/// The coverage span from a prior iteration; typically assigned from that iteration's `curr`.
|
||||
|
|
@ -229,7 +229,7 @@ impl SpansRefiner {
|
|||
let curr = self.curr();
|
||||
|
||||
if prev.is_mergeable(curr) {
|
||||
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
|
||||
debug!(?prev, "curr will be merged into prev");
|
||||
let curr = self.take_curr();
|
||||
self.prev_mut().merge_from(&curr);
|
||||
} else if prev.span.hi() <= curr.span.lo() {
|
||||
|
|
@ -238,15 +238,13 @@ impl SpansRefiner {
|
|||
);
|
||||
let prev = self.take_prev().into_refined();
|
||||
self.refined_spans.push(prev);
|
||||
} else if prev.is_closure {
|
||||
} else if prev.is_hole {
|
||||
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
|
||||
// next iter
|
||||
debug!(
|
||||
" curr overlaps a closure (prev). Drop curr and keep prev for next iter. prev={prev:?}",
|
||||
);
|
||||
debug!(?prev, "prev (a hole) overlaps curr, so discarding curr");
|
||||
self.take_curr(); // Discards curr.
|
||||
} else if curr.is_closure {
|
||||
self.carve_out_span_for_closure();
|
||||
} else if curr.is_hole {
|
||||
self.carve_out_span_for_hole();
|
||||
} else {
|
||||
self.cutoff_prev_at_overlapping_curr();
|
||||
}
|
||||
|
|
@ -270,10 +268,9 @@ impl SpansRefiner {
|
|||
}
|
||||
});
|
||||
|
||||
// Remove spans derived from closures, originally added to ensure the coverage
|
||||
// regions for the current function leave room for the closure's own coverage regions
|
||||
// (injected separately, from the closure's own MIR).
|
||||
self.refined_spans.retain(|covspan| !covspan.is_closure);
|
||||
// Discard hole spans, since their purpose was to carve out chunks from
|
||||
// other spans, but we don't want the holes themselves in the final mappings.
|
||||
self.refined_spans.retain(|covspan| !covspan.is_hole);
|
||||
self.refined_spans
|
||||
}
|
||||
|
||||
|
|
@ -316,48 +313,43 @@ impl SpansRefiner {
|
|||
{
|
||||
// Skip curr because prev has already advanced beyond the end of curr.
|
||||
// This can only happen if a prior iteration updated `prev` to skip past
|
||||
// a region of code, such as skipping past a closure.
|
||||
debug!(
|
||||
" prev.span starts after curr.span, so curr will be dropped (skipping past \
|
||||
closure?); prev={prev:?}",
|
||||
);
|
||||
// a region of code, such as skipping past a hole.
|
||||
debug!(?prev, "prev.span starts after curr.span, so curr will be dropped");
|
||||
} else {
|
||||
self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure));
|
||||
self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_hole));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// If `prev`s span extends left of the closure (`curr`), carve out the closure's span from
|
||||
/// `prev`'s span. (The closure's coverage counters will be injected when processing the
|
||||
/// closure's own MIR.) Add the portion of the span to the left of the closure; and if the span
|
||||
/// extends to the right of the closure, update `prev` to that portion of the span. For any
|
||||
/// `pending_dups`, repeat the same process.
|
||||
fn carve_out_span_for_closure(&mut self) {
|
||||
/// If `prev`s span extends left of the hole (`curr`), carve out the hole's span from
|
||||
/// `prev`'s span. Add the portion of the span to the left of the hole; and if the span
|
||||
/// extends to the right of the hole, update `prev` to that portion of the span.
|
||||
fn carve_out_span_for_hole(&mut self) {
|
||||
let prev = self.prev();
|
||||
let curr = self.curr();
|
||||
|
||||
let left_cutoff = curr.span.lo();
|
||||
let right_cutoff = curr.span.hi();
|
||||
let has_pre_closure_span = prev.span.lo() < right_cutoff;
|
||||
let has_post_closure_span = prev.span.hi() > right_cutoff;
|
||||
let has_pre_hole_span = prev.span.lo() < right_cutoff;
|
||||
let has_post_hole_span = prev.span.hi() > right_cutoff;
|
||||
|
||||
if has_pre_closure_span {
|
||||
let mut pre_closure = self.prev().refined_copy();
|
||||
pre_closure.span = pre_closure.span.with_hi(left_cutoff);
|
||||
debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
|
||||
self.refined_spans.push(pre_closure);
|
||||
if has_pre_hole_span {
|
||||
let mut pre_hole = prev.refined_copy();
|
||||
pre_hole.span = pre_hole.span.with_hi(left_cutoff);
|
||||
debug!(?pre_hole, "prev overlaps a hole; adding pre-hole span");
|
||||
self.refined_spans.push(pre_hole);
|
||||
}
|
||||
|
||||
if has_post_closure_span {
|
||||
// Mutate `prev.span` to start after the closure (and discard curr).
|
||||
if has_post_hole_span {
|
||||
// Mutate `prev.span` to start after the hole (and discard curr).
|
||||
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
|
||||
debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev());
|
||||
debug!(prev=?self.prev(), "mutated prev to start after the hole");
|
||||
|
||||
// Prevent this curr from becoming prev.
|
||||
let closure_covspan = self.take_curr().into_refined();
|
||||
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
|
||||
let hole_covspan = self.take_curr().into_refined();
|
||||
self.refined_spans.push(hole_covspan); // since self.prev() was already updated
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,14 +52,14 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
|||
// - Span A extends further left, or
|
||||
// - Both have the same start and span A extends further right
|
||||
.then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
|
||||
// If two spans have the same lo & hi, put closure spans first,
|
||||
// as they take precedence over non-closure spans.
|
||||
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
|
||||
// If two spans have the same lo & hi, put hole spans first,
|
||||
// as they take precedence over non-hole spans.
|
||||
.then_with(|| Ord::cmp(&a.is_hole, &b.is_hole).reverse())
|
||||
// After deduplication, we want to keep only the most-dominated BCB.
|
||||
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
||||
});
|
||||
|
||||
// Among covspans with the same span, keep only one. Closure spans take
|
||||
// Among covspans with the same span, keep only one. Hole spans take
|
||||
// precedence, otherwise keep the one with the most-dominated BCB.
|
||||
// (Ideally we should try to preserve _all_ non-dominating BCBs, but that
|
||||
// requires a lot more complexity in the span refiner, for little benefit.)
|
||||
|
|
@ -78,8 +78,8 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
|||
fn remove_unwanted_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
||||
let mut seen_macro_spans = FxHashSet::default();
|
||||
initial_spans.retain(|covspan| {
|
||||
// Ignore (retain) closure spans and non-macro-expansion spans.
|
||||
if covspan.is_closure || covspan.visible_macro.is_none() {
|
||||
// Ignore (retain) hole spans and non-macro-expansion spans.
|
||||
if covspan.is_hole || covspan.visible_macro.is_none() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
|||
let mut extra_spans = vec![];
|
||||
|
||||
initial_spans.retain(|covspan| {
|
||||
if covspan.is_closure {
|
||||
if covspan.is_hole {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
|||
return true;
|
||||
}
|
||||
|
||||
assert!(!covspan.is_closure);
|
||||
assert!(!covspan.is_hole);
|
||||
extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb, false));
|
||||
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb, false));
|
||||
false // Discard the original covspan that we just split.
|
||||
|
|
@ -148,6 +148,8 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
|
|||
let expn_span = filtered_statement_span(statement)?;
|
||||
let (span, visible_macro) = unexpand(expn_span)?;
|
||||
|
||||
// A statement that looks like the assignment of a closure expression
|
||||
// is treated as a "hole" span, to be carved out of other spans.
|
||||
Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_like(statement)))
|
||||
});
|
||||
|
||||
|
|
@ -336,7 +338,10 @@ pub(super) struct SpanFromMir {
|
|||
pub(super) span: Span,
|
||||
visible_macro: Option<Symbol>,
|
||||
pub(super) bcb: BasicCoverageBlock,
|
||||
pub(super) is_closure: bool,
|
||||
/// If true, this covspan represents a "hole" that should be carved out
|
||||
/// from other spans, e.g. because it represents a closure expression that
|
||||
/// will be instrumented separately as its own function.
|
||||
pub(super) is_hole: bool,
|
||||
}
|
||||
|
||||
impl SpanFromMir {
|
||||
|
|
@ -348,8 +353,8 @@ impl SpanFromMir {
|
|||
span: Span,
|
||||
visible_macro: Option<Symbol>,
|
||||
bcb: BasicCoverageBlock,
|
||||
is_closure: bool,
|
||||
is_hole: bool,
|
||||
) -> Self {
|
||||
Self { span, visible_macro, bcb, is_closure }
|
||||
Self { span, visible_macro, bcb, is_hole }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use rustc_middle::query::Providers;
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::InliningThreshold;
|
||||
use rustc_session::config::OptLevel;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.cross_crate_inlinable = cross_crate_inlinable;
|
||||
|
|
@ -34,6 +35,14 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
|||
return true;
|
||||
}
|
||||
|
||||
if tcx.has_attr(def_id, sym::rustc_intrinsic) {
|
||||
// Intrinsic fallback bodies are always cross-crate inlineable.
|
||||
// To ensure that the MIR inliner doesn't cluelessly try to inline fallback
|
||||
// bodies even when the backend would implement something better, we stop
|
||||
// the MIR inliner from ever inlining an intrinsic.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Obey source annotations first; this is important because it means we can use
|
||||
// #[inline(never)] to force code generation.
|
||||
match codegen_fn_attrs.inline {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use rustc_middle::ty::TypeVisitableExt;
|
|||
use rustc_middle::ty::{self, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_session::config::OptLevel;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::sym;
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
|
|
@ -170,6 +171,13 @@ impl<'tcx> Inliner<'tcx> {
|
|||
let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id());
|
||||
self.check_codegen_attributes(callsite, callee_attrs, cross_crate_inlinable)?;
|
||||
|
||||
// Intrinsic fallback bodies are automatically made cross-crate inlineable,
|
||||
// but at this stage we don't know whether codegen knows the intrinsic,
|
||||
// so just conservatively don't inline it.
|
||||
if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_intrinsic) {
|
||||
return Err("Callee is an intrinsic, do not inline fallback bodies");
|
||||
}
|
||||
|
||||
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
|
||||
let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() };
|
||||
let destination_ty = destination.ty(&caller_body.local_decls, self.tcx).ty;
|
||||
|
|
@ -413,6 +421,10 @@ impl<'tcx> Inliner<'tcx> {
|
|||
callee_attrs: &CodegenFnAttrs,
|
||||
cross_crate_inlinable: bool,
|
||||
) -> Result<(), &'static str> {
|
||||
if self.tcx.has_attr(callsite.callee.def_id(), sym::rustc_no_mir_inline) {
|
||||
return Err("#[rustc_no_mir_inline]");
|
||||
}
|
||||
|
||||
if let InlineAttr::Never = callee_attrs.inline {
|
||||
return Err("never inline hint");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,17 +482,40 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
match op {
|
||||
BinOp::Div | BinOp::Rem => {
|
||||
if lhs_ty.is_integral() {
|
||||
let sz = lhs_ty.primitive_size(self.tcx);
|
||||
// Integer division: the RHS must be a non-zero const.
|
||||
let const_val = match rhs {
|
||||
let rhs_val = match rhs {
|
||||
Operand::Constant(c) => {
|
||||
c.const_.try_eval_bits(self.tcx, self.param_env)
|
||||
c.const_.try_eval_scalar_int(self.tcx, self.param_env)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
match const_val {
|
||||
match rhs_val.map(|x| x.try_to_uint(sz).unwrap()) {
|
||||
// for the zero test, int vs uint does not matter
|
||||
Some(x) if x != 0 => {} // okay
|
||||
_ => return Err(Unpromotable), // value not known or 0 -- not okay
|
||||
}
|
||||
// Furthermore, for signed divison, we also have to exclude `int::MIN / -1`.
|
||||
if lhs_ty.is_signed() {
|
||||
match rhs_val.map(|x| x.try_to_int(sz).unwrap()) {
|
||||
Some(-1) | None => {
|
||||
// The RHS is -1 or unknown, so we have to be careful.
|
||||
// But is the LHS int::MIN?
|
||||
let lhs_val = match lhs {
|
||||
Operand::Constant(c) => c
|
||||
.const_
|
||||
.try_eval_scalar_int(self.tcx, self.param_env),
|
||||
_ => None,
|
||||
};
|
||||
let lhs_min = sz.signed_int_min();
|
||||
match lhs_val.map(|x| x.try_to_int(sz).unwrap()) {
|
||||
Some(x) if x != lhs_min => {} // okay
|
||||
_ => return Err(Unpromotable), // value not known or int::MIN -- not okay
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The remaining operations can never fail.
|
||||
|
|
|
|||
|
|
@ -1073,9 +1073,9 @@ pub(crate) struct ExpectedIdentifier {
|
|||
pub help_cannot_start_number: Option<HelpIdentifierStartsWithNumber>,
|
||||
}
|
||||
|
||||
impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier {
|
||||
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedIdentifier {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
|
||||
let token_descr = TokenDescription::from_token(&self.token);
|
||||
|
||||
let mut diag = DiagnosticBuilder::new(
|
||||
|
|
@ -1133,9 +1133,9 @@ pub(crate) struct ExpectedSemi {
|
|||
pub sugg: ExpectedSemiSugg,
|
||||
}
|
||||
|
||||
impl<'a> IntoDiagnostic<'a> for ExpectedSemi {
|
||||
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for ExpectedSemi {
|
||||
#[track_caller]
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
|
||||
fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
|
||||
let token_descr = TokenDescription::from_token(&self.token);
|
||||
|
||||
let mut diag = DiagnosticBuilder::new(
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ pub fn parse_meta<'a>(sess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Meta
|
|||
// results in `ast::ExprKind::Err`. In that case we delay
|
||||
// the error because an earlier error will have already
|
||||
// been reported.
|
||||
let msg = format!("attribute value must be a literal");
|
||||
let msg = "attribute value must be a literal";
|
||||
let mut err = sess.dcx.struct_span_err(expr.span, msg);
|
||||
if let ast::ExprKind::Err = expr.kind {
|
||||
err.downgrade_to_delayed_bug();
|
||||
|
|
|
|||
|
|
@ -694,18 +694,14 @@ impl<Cx: TypeCx> Clone for Constructor<Cx> {
|
|||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Constructor::Struct => Constructor::Struct,
|
||||
Constructor::Variant(idx) => Constructor::Variant(idx.clone()),
|
||||
Constructor::Variant(idx) => Constructor::Variant(*idx),
|
||||
Constructor::Ref => Constructor::Ref,
|
||||
Constructor::Slice(slice) => Constructor::Slice(slice.clone()),
|
||||
Constructor::Slice(slice) => Constructor::Slice(*slice),
|
||||
Constructor::UnionField => Constructor::UnionField,
|
||||
Constructor::Bool(b) => Constructor::Bool(b.clone()),
|
||||
Constructor::IntRange(range) => Constructor::IntRange(range.clone()),
|
||||
Constructor::F32Range(lo, hi, end) => {
|
||||
Constructor::F32Range(lo.clone(), hi.clone(), end.clone())
|
||||
}
|
||||
Constructor::F64Range(lo, hi, end) => {
|
||||
Constructor::F64Range(lo.clone(), hi.clone(), end.clone())
|
||||
}
|
||||
Constructor::Bool(b) => Constructor::Bool(*b),
|
||||
Constructor::IntRange(range) => Constructor::IntRange(*range),
|
||||
Constructor::F32Range(lo, hi, end) => Constructor::F32Range(lo.clone(), *hi, *end),
|
||||
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(lo.clone(), *hi, *end),
|
||||
Constructor::Str(value) => Constructor::Str(value.clone()),
|
||||
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
|
||||
Constructor::Or => Constructor::Or,
|
||||
|
|
|
|||
|
|
@ -988,7 +988,10 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
|
|||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
|
||||
if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind {
|
||||
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
|
||||
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
|
||||
let Some(adt) = self.typeck_results().expr_ty(expr).ty_adt_def() else {
|
||||
self.tcx.dcx().span_delayed_bug(expr.span, "no adt_def for expression");
|
||||
return;
|
||||
};
|
||||
let variant = adt.variant_of_res(res);
|
||||
if let Some(base) = *base {
|
||||
// If the expression uses FRU we need to make sure all the unmentioned fields
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ impl SerializedDepGraph {
|
|||
|
||||
/// A packed representation of an edge's start index and byte width.
|
||||
///
|
||||
/// This is packed by stealing 2 bits from the start index, which means we only accomodate edge
|
||||
/// This is packed by stealing 2 bits from the start index, which means we only accommodate edge
|
||||
/// data arrays up to a quarter of our address space. Which seems fine.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct EdgeHeader {
|
||||
|
|
|
|||
|
|
@ -1582,7 +1582,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
|||
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
let pad = match field_ids {
|
||||
Some(field_ids) if field_ids.is_empty() => "",
|
||||
Some([]) => "",
|
||||
_ => " ",
|
||||
};
|
||||
err.span_suggestion(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue