Merge from rustc

This commit is contained in:
Ralf Jung 2024-02-25 10:41:19 +01:00
commit 87042866a3
2258 changed files with 11565 additions and 9075 deletions

View file

@ -20,3 +20,6 @@ f97fddab91fbf290ea5b691fe355d6f915220b6e
cc907f80b95c6ec530c5ee1b05b044a468f07eca
# format let-chains
b2d2184edea578109a48ec3d8decbee5948e8f35
# test directives migration
6e48b96692d63a79a14563f27fe5185f122434f8
ec2cc761bc7067712ecc7734502f703fe3b024c8

View file

@ -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",

View file

@ -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),

View file

@ -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;
}
}

View file

@ -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

View file

@ -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(),

View file

@ -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))
}
}

View file

@ -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()));
}
});
});

View file

@ -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);
}

View file

@ -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,
}

View file

@ -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(),
});
}
}
}

View file

@ -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}."
),

View file

@ -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() {

View file

@ -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!();
}

View file

@ -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) => {

View file

@ -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,
)

View file

@ -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) }
}

View file

@ -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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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;

View file

@ -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,
);
}
}

View file

@ -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}`

View file

@ -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:
///

View file

@ -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);
}

View file

@ -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,

View file

@ -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);

View file

@ -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();

View file

@ -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
}

View file

@ -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");
}
}

View file

@ -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 => {

View file

@ -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
}

View file

@ -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);

View file

@ -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)
});

View file

@ -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);

View file

@ -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();

View file

@ -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) {

View file

@ -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]

View file

@ -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 {

View file

@ -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:

View file

@ -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.

View file

@ -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;

View file

@ -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())
}
}

View file

@ -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 {

View file

@ -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)),
_ => {

View file

@ -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, _) => {

View file

@ -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:?}"),
);

View file

@ -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();
}
}
}
}

View file

@ -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"))
});
}
}

View file

@ -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 {

View file

@ -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);

View file

@ -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

View file

@ -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| {

View file

@ -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(..), .. }))));

View file

@ -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(())
}
}
}

View file

@ -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 {

View file

@ -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 dont take self as a parameter and
// they are not methods because they dont 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 dont take self as a parameter and they are
// not methods because they dont 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 #{}: `{}`",

View file

@ -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;
}

View file

@ -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,
);
}
}
}
}

View file

@ -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,

View file

@ -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);

View file

@ -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 {

View file

@ -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)
}
}

View file

@ -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 {

View file

@ -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()

View file

@ -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 {

View file

@ -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),
}
}

View file

@ -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)
}

View file

@ -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,

View file

@ -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>),

View file

@ -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

View file

@ -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)]

View file

@ -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 {

View file

@ -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) {

View file

@ -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,
})
}
}

View file

@ -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)]

View file

@ -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

View file

@ -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,

View file

@ -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 {

View file

@ -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

View file

@ -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),

View file

@ -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()))
}

View file

@ -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

View file

@ -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(())
}
}

View file

@ -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>,

View file

@ -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 }
}
}

View file

@ -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,

View file

@ -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)
}

View file

@ -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(

View file

@ -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
}
}

View file

@ -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 }
}
}

View file

@ -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 {

View file

@ -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");
}

View file

@ -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.

View file

@ -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(

View file

@ -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();

View file

@ -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,

View file

@ -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

View file

@ -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 {

View file

@ -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