mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 14:07:04 +00:00
Auto merge of #101195 - Dylan-DPC:rollup-rhjaz6r, r=Dylan-DPC
Rollup of 5 pull requests Successful merges: - #99517 (Display raw pointer as *{mut,const} T instead of *-ptr in errors) - #99928 (Do not leak type variables from opaque type relation) - #100473 (Attempt to normalize `FnDef` signature in `InferCtxt::cmp`) - #100653 (Move the cast_float_to_int fallback code to GCC) - #100941 (Point at the string inside literal and mention if we need string inte…) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
fce6a7d66e
@ -3370,7 +3370,6 @@ dependencies = [
|
||||
"object 0.29.0",
|
||||
"pathdiff",
|
||||
"regex",
|
||||
"rustc_apfloat",
|
||||
"rustc_arena",
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
@ -15,8 +15,11 @@ use gccjit::{
|
||||
Type,
|
||||
UnaryOp,
|
||||
};
|
||||
use rustc_apfloat::{ieee, Float, Round, Status};
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope};
|
||||
use rustc_codegen_ssa::common::{
|
||||
AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind,
|
||||
};
|
||||
use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
|
||||
use rustc_codegen_ssa::mir::place::PlaceRef;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
@ -31,6 +34,7 @@ use rustc_codegen_ssa::traits::{
|
||||
StaticBuilderMethods,
|
||||
};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
|
||||
use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_span::Span;
|
||||
@ -1271,12 +1275,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
val
|
||||
}
|
||||
|
||||
fn fptoui_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
|
||||
None
|
||||
fn fptoui_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||
self.fptoint_sat(false, val, dest_ty)
|
||||
}
|
||||
|
||||
fn fptosi_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option<RValue<'gcc>> {
|
||||
None
|
||||
fn fptosi_sat(&mut self, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||
self.fptoint_sat(true, val, dest_ty)
|
||||
}
|
||||
|
||||
fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) {
|
||||
@ -1285,6 +1289,166 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
||||
fn fptoint_sat(&mut self, signed: bool, val: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let (float_ty, int_ty) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
|
||||
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
|
||||
(self.cx.element_type(src_ty), self.cx.element_type(dest_ty))
|
||||
} else {
|
||||
(src_ty, dest_ty)
|
||||
};
|
||||
|
||||
// FIXME(jistone): the following was originally the fallback SSA implementation, before LLVM 13
|
||||
// added native `fptosi.sat` and `fptoui.sat` conversions, but it was used by GCC as well.
|
||||
// Now that LLVM always relies on its own, the code has been moved to GCC, but the comments are
|
||||
// still LLVM-specific. This should be updated, and use better GCC specifics if possible.
|
||||
|
||||
let int_width = self.cx.int_width(int_ty);
|
||||
let float_width = self.cx.float_width(float_ty);
|
||||
// LLVM's fpto[su]i returns undef when the input val is infinite, NaN, or does not fit into the
|
||||
// destination integer type after rounding towards zero. This `undef` value can cause UB in
|
||||
// safe code (see issue #10184), so we implement a saturating conversion on top of it:
|
||||
// Semantically, the mathematical value of the input is rounded towards zero to the next
|
||||
// mathematical integer, and then the result is clamped into the range of the destination
|
||||
// integer type. Positive and negative infinity are mapped to the maximum and minimum value of
|
||||
// the destination integer type. NaN is mapped to 0.
|
||||
//
|
||||
// Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to
|
||||
// a value representable in int_ty.
|
||||
// They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits.
|
||||
// Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two.
|
||||
// int_ty::MIN, however, is either zero or a negative power of two and is thus exactly
|
||||
// representable. Note that this only works if float_ty's exponent range is sufficiently large.
|
||||
// f16 or 256 bit integers would break this property. Right now the smallest float type is f32
|
||||
// with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127.
|
||||
// On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
|
||||
// we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
|
||||
// This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
|
||||
let int_max = |signed: bool, int_width: u64| -> u128 {
|
||||
let shift_amount = 128 - int_width;
|
||||
if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
|
||||
};
|
||||
let int_min = |signed: bool, int_width: u64| -> i128 {
|
||||
if signed { i128::MIN >> (128 - int_width) } else { 0 }
|
||||
};
|
||||
|
||||
let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) {
|
||||
let rounded_min =
|
||||
ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
let rounded_max =
|
||||
ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
assert!(rounded_max.value.is_finite());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) {
|
||||
let rounded_min =
|
||||
ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
let rounded_max =
|
||||
ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
assert!(rounded_max.value.is_finite());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
// To implement saturation, we perform the following steps:
|
||||
//
|
||||
// 1. Cast val to an integer with fpto[su]i. This may result in undef.
|
||||
// 2. Compare val to f_min and f_max, and use the comparison results to select:
|
||||
// a) int_ty::MIN if val < f_min or val is NaN
|
||||
// b) int_ty::MAX if val > f_max
|
||||
// c) the result of fpto[su]i otherwise
|
||||
// 3. If val is NaN, return 0.0, otherwise return the result of step 2.
|
||||
//
|
||||
// This avoids resulting undef because values in range [f_min, f_max] by definition fit into the
|
||||
// destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of
|
||||
// undef does not introduce any non-determinism either.
|
||||
// More importantly, the above procedure correctly implements saturating conversion.
|
||||
// Proof (sketch):
|
||||
// If val is NaN, 0 is returned by definition.
|
||||
// Otherwise, val is finite or infinite and thus can be compared with f_min and f_max.
|
||||
// This yields three cases to consider:
|
||||
// (1) if val in [f_min, f_max], the result of fpto[su]i is returned, which agrees with
|
||||
// saturating conversion for inputs in that range.
|
||||
// (2) if val > f_max, then val is larger than int_ty::MAX. This holds even if f_max is rounded
|
||||
// (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger
|
||||
// than int_ty::MAX. Because val is larger than int_ty::MAX, the return value of int_ty::MAX
|
||||
// is correct.
|
||||
// (3) if val < f_min, then val is smaller than int_ty::MIN. As shown earlier, f_min exactly equals
|
||||
// int_ty::MIN and therefore the return value of int_ty::MIN is correct.
|
||||
// QED.
|
||||
|
||||
let float_bits_to_llval = |bx: &mut Self, bits| {
|
||||
let bits_llval = match float_width {
|
||||
32 => bx.cx().const_u32(bits as u32),
|
||||
64 => bx.cx().const_u64(bits as u64),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
bx.bitcast(bits_llval, float_ty)
|
||||
};
|
||||
let (f_min, f_max) = match float_width {
|
||||
32 => compute_clamp_bounds_single(signed, int_width),
|
||||
64 => compute_clamp_bounds_double(signed, int_width),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
let f_min = float_bits_to_llval(self, f_min);
|
||||
let f_max = float_bits_to_llval(self, f_max);
|
||||
let int_max = self.cx.const_uint_big(int_ty, int_max(signed, int_width));
|
||||
let int_min = self.cx.const_uint_big(int_ty, int_min(signed, int_width) as u128);
|
||||
let zero = self.cx.const_uint(int_ty, 0);
|
||||
|
||||
// If we're working with vectors, constants must be "splatted": the constant is duplicated
|
||||
// into each lane of the vector. The algorithm stays the same, we are just using the
|
||||
// same constant across all lanes.
|
||||
let maybe_splat = |bx: &mut Self, val| {
|
||||
if bx.cx().type_kind(dest_ty) == TypeKind::Vector {
|
||||
bx.vector_splat(bx.vector_length(dest_ty), val)
|
||||
} else {
|
||||
val
|
||||
}
|
||||
};
|
||||
let f_min = maybe_splat(self, f_min);
|
||||
let f_max = maybe_splat(self, f_max);
|
||||
let int_max = maybe_splat(self, int_max);
|
||||
let int_min = maybe_splat(self, int_min);
|
||||
let zero = maybe_splat(self, zero);
|
||||
|
||||
// Step 1 ...
|
||||
let fptosui_result = if signed { self.fptosi(val, dest_ty) } else { self.fptoui(val, dest_ty) };
|
||||
let less_or_nan = self.fcmp(RealPredicate::RealULT, val, f_min);
|
||||
let greater = self.fcmp(RealPredicate::RealOGT, val, f_max);
|
||||
|
||||
// Step 2: We use two comparisons and two selects, with %s1 being the
|
||||
// result:
|
||||
// %less_or_nan = fcmp ult %val, %f_min
|
||||
// %greater = fcmp olt %val, %f_max
|
||||
// %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
|
||||
// %s1 = select %greater, int_ty::MAX, %s0
|
||||
// Note that %less_or_nan uses an *unordered* comparison. This
|
||||
// comparison is true if the operands are not comparable (i.e., if val is
|
||||
// NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
|
||||
// val is NaN.
|
||||
//
|
||||
// Performance note: Unordered comparison can be lowered to a "flipped"
|
||||
// comparison and a negation, and the negation can be merged into the
|
||||
// select. Therefore, it not necessarily any more expensive than an
|
||||
// ordered ("normal") comparison. Whether these optimizations will be
|
||||
// performed is ultimately up to the backend, but at least x86 does
|
||||
// perform them.
|
||||
let s0 = self.select(less_or_nan, int_min, fptosui_result);
|
||||
let s1 = self.select(greater, int_max, s0);
|
||||
|
||||
// Step 3: NaN replacement.
|
||||
// For unsigned types, the above step already yielded int_ty::MIN == 0 if val is NaN.
|
||||
// Therefore we only need to execute this step for signed integer types.
|
||||
if signed {
|
||||
// LLVM has no isNaN predicate, so we use (val == val) instead
|
||||
let cmp = self.fcmp(RealPredicate::RealOEQ, val, val);
|
||||
self.select(cmp, s1, zero)
|
||||
} else {
|
||||
s1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="master")]
|
||||
pub fn shuffle_vector(&mut self, v1: RValue<'gcc>, v2: RValue<'gcc>, mask: RValue<'gcc>) -> RValue<'gcc> {
|
||||
let struct_type = mask.get_type().is_struct().expect("mask of struct type");
|
||||
|
@ -19,6 +19,7 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![warn(unused_lifetimes)]
|
||||
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_codegen_ssa;
|
||||
extern crate rustc_data_structures;
|
||||
|
@ -725,11 +725,11 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
|
||||
unsafe { llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty, UNNAMED) }
|
||||
}
|
||||
|
||||
fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
|
||||
fn fptoui_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
self.fptoint_sat(false, val, dest_ty)
|
||||
}
|
||||
|
||||
fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> Option<&'ll Value> {
|
||||
fn fptosi_sat(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
self.fptoint_sat(true, val, dest_ty)
|
||||
}
|
||||
|
||||
@ -1429,12 +1429,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn fptoint_sat(
|
||||
&mut self,
|
||||
signed: bool,
|
||||
val: &'ll Value,
|
||||
dest_ty: &'ll Type,
|
||||
) -> Option<&'ll Value> {
|
||||
fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value {
|
||||
let src_ty = self.cx.val_ty(val);
|
||||
let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector {
|
||||
assert_eq!(self.cx.vector_length(src_ty), self.cx.vector_length(dest_ty));
|
||||
@ -1459,7 +1454,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
|
||||
format!("llvm.{}.sat.i{}.f{}", instr, int_width, float_width)
|
||||
};
|
||||
let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty));
|
||||
Some(self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None))
|
||||
self.call(self.type_func(&[src_ty], dest_ty), f, &[val], None)
|
||||
}
|
||||
|
||||
pub(crate) fn landing_pad(
|
||||
|
@ -26,7 +26,6 @@ rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_apfloat = { path = "../rustc_apfloat" }
|
||||
rustc_attr = { path = "../rustc_attr" }
|
||||
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::abi::AbiBuilderMethods;
|
||||
use super::asm::AsmBuilderMethods;
|
||||
use super::consts::ConstMethods;
|
||||
use super::coverageinfo::CoverageInfoBuilderMethods;
|
||||
use super::debuginfo::DebugInfoBuilderMethods;
|
||||
use super::intrinsic::IntrinsicCallMethods;
|
||||
@ -15,7 +14,6 @@ use crate::mir::operand::OperandRef;
|
||||
use crate::mir::place::PlaceRef;
|
||||
use crate::MemFlags;
|
||||
|
||||
use rustc_apfloat::{ieee, Float, Round, Status};
|
||||
use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Span;
|
||||
@ -188,8 +186,8 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
|
||||
fn trunc(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn sext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
|
||||
fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Option<Self::Value>;
|
||||
fn fptoui_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptosi_sat(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptoui(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn fptosi(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
fn uitofp(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
|
||||
@ -223,156 +221,7 @@ pub trait BuilderMethods<'a, 'tcx>:
|
||||
return if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) };
|
||||
}
|
||||
|
||||
let try_sat_result =
|
||||
if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) };
|
||||
if let Some(try_sat_result) = try_sat_result {
|
||||
return try_sat_result;
|
||||
}
|
||||
|
||||
let int_width = self.cx().int_width(int_ty);
|
||||
let float_width = self.cx().float_width(float_ty);
|
||||
// LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the
|
||||
// destination integer type after rounding towards zero. This `undef` value can cause UB in
|
||||
// safe code (see issue #10184), so we implement a saturating conversion on top of it:
|
||||
// Semantically, the mathematical value of the input is rounded towards zero to the next
|
||||
// mathematical integer, and then the result is clamped into the range of the destination
|
||||
// integer type. Positive and negative infinity are mapped to the maximum and minimum value of
|
||||
// the destination integer type. NaN is mapped to 0.
|
||||
//
|
||||
// Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to
|
||||
// a value representable in int_ty.
|
||||
// They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits.
|
||||
// Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two.
|
||||
// int_ty::MIN, however, is either zero or a negative power of two and is thus exactly
|
||||
// representable. Note that this only works if float_ty's exponent range is sufficiently large.
|
||||
// f16 or 256 bit integers would break this property. Right now the smallest float type is f32
|
||||
// with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127.
|
||||
// On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because
|
||||
// we're rounding towards zero, we just get float_ty::MAX (which is always an integer).
|
||||
// This already happens today with u128::MAX = 2^128 - 1 > f32::MAX.
|
||||
let int_max = |signed: bool, int_width: u64| -> u128 {
|
||||
let shift_amount = 128 - int_width;
|
||||
if signed { i128::MAX as u128 >> shift_amount } else { u128::MAX >> shift_amount }
|
||||
};
|
||||
let int_min = |signed: bool, int_width: u64| -> i128 {
|
||||
if signed { i128::MIN >> (128 - int_width) } else { 0 }
|
||||
};
|
||||
|
||||
let compute_clamp_bounds_single = |signed: bool, int_width: u64| -> (u128, u128) {
|
||||
let rounded_min =
|
||||
ieee::Single::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
let rounded_max =
|
||||
ieee::Single::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
assert!(rounded_max.value.is_finite());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
let compute_clamp_bounds_double = |signed: bool, int_width: u64| -> (u128, u128) {
|
||||
let rounded_min =
|
||||
ieee::Double::from_i128_r(int_min(signed, int_width), Round::TowardZero);
|
||||
assert_eq!(rounded_min.status, Status::OK);
|
||||
let rounded_max =
|
||||
ieee::Double::from_u128_r(int_max(signed, int_width), Round::TowardZero);
|
||||
assert!(rounded_max.value.is_finite());
|
||||
(rounded_min.value.to_bits(), rounded_max.value.to_bits())
|
||||
};
|
||||
// To implement saturation, we perform the following steps:
|
||||
//
|
||||
// 1. Cast x to an integer with fpto[su]i. This may result in undef.
|
||||
// 2. Compare x to f_min and f_max, and use the comparison results to select:
|
||||
// a) int_ty::MIN if x < f_min or x is NaN
|
||||
// b) int_ty::MAX if x > f_max
|
||||
// c) the result of fpto[su]i otherwise
|
||||
// 3. If x is NaN, return 0.0, otherwise return the result of step 2.
|
||||
//
|
||||
// This avoids resulting undef because values in range [f_min, f_max] by definition fit into the
|
||||
// destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of
|
||||
// undef does not introduce any non-determinism either.
|
||||
// More importantly, the above procedure correctly implements saturating conversion.
|
||||
// Proof (sketch):
|
||||
// If x is NaN, 0 is returned by definition.
|
||||
// Otherwise, x is finite or infinite and thus can be compared with f_min and f_max.
|
||||
// This yields three cases to consider:
|
||||
// (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with
|
||||
// saturating conversion for inputs in that range.
|
||||
// (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded
|
||||
// (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger
|
||||
// than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX
|
||||
// is correct.
|
||||
// (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals
|
||||
// int_ty::MIN and therefore the return value of int_ty::MIN is correct.
|
||||
// QED.
|
||||
|
||||
let float_bits_to_llval = |bx: &mut Self, bits| {
|
||||
let bits_llval = match float_width {
|
||||
32 => bx.cx().const_u32(bits as u32),
|
||||
64 => bx.cx().const_u64(bits as u64),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
bx.bitcast(bits_llval, float_ty)
|
||||
};
|
||||
let (f_min, f_max) = match float_width {
|
||||
32 => compute_clamp_bounds_single(signed, int_width),
|
||||
64 => compute_clamp_bounds_double(signed, int_width),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
let f_min = float_bits_to_llval(self, f_min);
|
||||
let f_max = float_bits_to_llval(self, f_max);
|
||||
let int_max = self.cx().const_uint_big(int_ty, int_max(signed, int_width));
|
||||
let int_min = self.cx().const_uint_big(int_ty, int_min(signed, int_width) as u128);
|
||||
let zero = self.cx().const_uint(int_ty, 0);
|
||||
|
||||
// If we're working with vectors, constants must be "splatted": the constant is duplicated
|
||||
// into each lane of the vector. The algorithm stays the same, we are just using the
|
||||
// same constant across all lanes.
|
||||
let maybe_splat = |bx: &mut Self, val| {
|
||||
if bx.cx().type_kind(dest_ty) == TypeKind::Vector {
|
||||
bx.vector_splat(bx.vector_length(dest_ty), val)
|
||||
} else {
|
||||
val
|
||||
}
|
||||
};
|
||||
let f_min = maybe_splat(self, f_min);
|
||||
let f_max = maybe_splat(self, f_max);
|
||||
let int_max = maybe_splat(self, int_max);
|
||||
let int_min = maybe_splat(self, int_min);
|
||||
let zero = maybe_splat(self, zero);
|
||||
|
||||
// Step 1 ...
|
||||
let fptosui_result = if signed { self.fptosi(x, dest_ty) } else { self.fptoui(x, dest_ty) };
|
||||
let less_or_nan = self.fcmp(RealPredicate::RealULT, x, f_min);
|
||||
let greater = self.fcmp(RealPredicate::RealOGT, x, f_max);
|
||||
|
||||
// Step 2: We use two comparisons and two selects, with %s1 being the
|
||||
// result:
|
||||
// %less_or_nan = fcmp ult %x, %f_min
|
||||
// %greater = fcmp olt %x, %f_max
|
||||
// %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result
|
||||
// %s1 = select %greater, int_ty::MAX, %s0
|
||||
// Note that %less_or_nan uses an *unordered* comparison. This
|
||||
// comparison is true if the operands are not comparable (i.e., if x is
|
||||
// NaN). The unordered comparison ensures that s1 becomes int_ty::MIN if
|
||||
// x is NaN.
|
||||
//
|
||||
// Performance note: Unordered comparison can be lowered to a "flipped"
|
||||
// comparison and a negation, and the negation can be merged into the
|
||||
// select. Therefore, it not necessarily any more expensive than an
|
||||
// ordered ("normal") comparison. Whether these optimizations will be
|
||||
// performed is ultimately up to the backend, but at least x86 does
|
||||
// perform them.
|
||||
let s0 = self.select(less_or_nan, int_min, fptosui_result);
|
||||
let s1 = self.select(greater, int_max, s0);
|
||||
|
||||
// Step 3: NaN replacement.
|
||||
// For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN.
|
||||
// Therefore we only need to execute this step for signed integer types.
|
||||
if signed {
|
||||
// LLVM has no isNaN predicate, so we use (x == x) instead
|
||||
let cmp = self.fcmp(RealPredicate::RealOEQ, x, x);
|
||||
self.select(cmp, s1, zero)
|
||||
} else {
|
||||
s1
|
||||
}
|
||||
if signed { self.fptosi_sat(x, dest_ty) } else { self.fptoui_sat(x, dest_ty) }
|
||||
}
|
||||
|
||||
fn icmp(&mut self, op: IntPredicate, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
|
||||
|
@ -78,6 +78,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
err_count_on_creation: self.err_count_on_creation,
|
||||
in_snapshot: self.in_snapshot.clone(),
|
||||
universe: self.universe.clone(),
|
||||
normalize_fn_sig_for_diagnostic: self
|
||||
.normalize_fn_sig_for_diagnostic
|
||||
.as_ref()
|
||||
.map(|f| f.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -961,12 +961,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> {
|
||||
if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic {
|
||||
normalize(self, sig)
|
||||
} else {
|
||||
sig
|
||||
}
|
||||
}
|
||||
|
||||
/// Given two `fn` signatures highlight only sub-parts that are different.
|
||||
fn cmp_fn_sig(
|
||||
&self,
|
||||
sig1: &ty::PolyFnSig<'tcx>,
|
||||
sig2: &ty::PolyFnSig<'tcx>,
|
||||
) -> (DiagnosticStyledString, DiagnosticStyledString) {
|
||||
let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1);
|
||||
let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2);
|
||||
|
||||
let get_lifetimes = |sig| {
|
||||
use rustc_hir::def::Namespace;
|
||||
let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
|
||||
|
@ -337,6 +337,9 @@ pub struct InferCtxt<'a, 'tcx> {
|
||||
/// when we enter into a higher-ranked (`for<..>`) type or trait
|
||||
/// bound.
|
||||
universe: Cell<ty::UniverseIndex>,
|
||||
|
||||
normalize_fn_sig_for_diagnostic:
|
||||
Option<Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
|
||||
}
|
||||
|
||||
/// See the `error_reporting` module for more details.
|
||||
@ -540,6 +543,8 @@ pub struct InferCtxtBuilder<'tcx> {
|
||||
defining_use_anchor: DefiningAnchor,
|
||||
considering_regions: bool,
|
||||
fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
|
||||
normalize_fn_sig_for_diagnostic:
|
||||
Option<Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>,
|
||||
}
|
||||
|
||||
pub trait TyCtxtInferExt<'tcx> {
|
||||
@ -553,6 +558,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
|
||||
defining_use_anchor: DefiningAnchor::Error,
|
||||
considering_regions: true,
|
||||
fresh_typeck_results: None,
|
||||
normalize_fn_sig_for_diagnostic: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -582,6 +588,14 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_normalize_fn_sig_for_diagnostic(
|
||||
mut self,
|
||||
fun: Lrc<dyn Fn(&InferCtxt<'_, 'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>,
|
||||
) -> Self {
|
||||
self.normalize_fn_sig_for_diagnostic = Some(fun);
|
||||
self
|
||||
}
|
||||
|
||||
/// Given a canonical value `C` as a starting point, create an
|
||||
/// inference context that contains each of the bound values
|
||||
/// within instantiated as a fresh variable. The `f` closure is
|
||||
@ -611,6 +625,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
defining_use_anchor,
|
||||
considering_regions,
|
||||
ref fresh_typeck_results,
|
||||
ref normalize_fn_sig_for_diagnostic,
|
||||
} = *self;
|
||||
let in_progress_typeck_results = fresh_typeck_results.as_ref();
|
||||
f(InferCtxt {
|
||||
@ -629,6 +644,9 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
|
||||
in_snapshot: Cell::new(false),
|
||||
skip_leak_check: Cell::new(false),
|
||||
universe: Cell::new(ty::UniverseIndex::ROOT),
|
||||
normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic
|
||||
.as_ref()
|
||||
.map(|f| f.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ use super::SubregionOrigin;
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use crate::traits::Obligation;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
use rustc_middle::ty::TyVar;
|
||||
@ -141,17 +142,27 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
Ok(infcx.tcx.mk_ty_var(var))
|
||||
};
|
||||
let (a, b) = if self.a_is_expected { (a, b) } else { (b, a) };
|
||||
let (a, b) = match (a.kind(), b.kind()) {
|
||||
let (ga, gb) = match (a.kind(), b.kind()) {
|
||||
(&ty::Opaque(..), _) => (a, generalize(b, true)?),
|
||||
(_, &ty::Opaque(..)) => (generalize(a, false)?, b),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.fields.obligations.extend(
|
||||
infcx
|
||||
.handle_opaque_type(a, b, true, &self.fields.trace.cause, self.param_env())?
|
||||
.handle_opaque_type(ga, gb, true, &self.fields.trace.cause, self.param_env())
|
||||
// Don't leak any generalized type variables out of this
|
||||
// subtyping relation in the case of a type error.
|
||||
.map_err(|err| {
|
||||
let (ga, gb) = self.fields.infcx.resolve_vars_if_possible((ga, gb));
|
||||
if let TypeError::Sorts(sorts) = err && sorts.expected == ga && sorts.found == gb {
|
||||
TypeError::Sorts(ExpectedFound { expected: a, found: b })
|
||||
} else {
|
||||
err
|
||||
}
|
||||
})?
|
||||
.obligations,
|
||||
);
|
||||
Ok(a)
|
||||
Ok(ga)
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -276,10 +276,23 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
ty::Slice(ty) if ty.is_simple_ty() => format!("slice `{}`", self).into(),
|
||||
ty::Slice(_) => "slice".into(),
|
||||
ty::RawPtr(_) => "*-ptr".into(),
|
||||
ty::RawPtr(tymut) => {
|
||||
let tymut_string = match tymut.mutbl {
|
||||
hir::Mutability::Mut => tymut.to_string(),
|
||||
hir::Mutability::Not => format!("const {}", tymut.ty),
|
||||
};
|
||||
|
||||
if tymut_string != "_" && (tymut.ty.is_simple_text() || tymut_string.len() < "const raw pointer".len()) {
|
||||
format!("`*{}`", tymut_string).into()
|
||||
} else {
|
||||
// Unknown type name, it's long or has type arguments
|
||||
"raw pointer".into()
|
||||
}
|
||||
},
|
||||
ty::Ref(_, ty, mutbl) => {
|
||||
let tymut = ty::TypeAndMut { ty, mutbl };
|
||||
let tymut_string = tymut.to_string();
|
||||
|
||||
if tymut_string != "_"
|
||||
&& (ty.is_simple_text() || tymut_string.len() < "mutable reference".len())
|
||||
{
|
||||
|
@ -229,6 +229,19 @@ enum VarKind {
|
||||
Upvar(HirId, Symbol),
|
||||
}
|
||||
|
||||
struct CollectLitsVisitor<'tcx> {
|
||||
lit_exprs: Vec<&'tcx hir::Expr<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CollectLitsVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if let hir::ExprKind::Lit(_) = expr.kind {
|
||||
self.lit_exprs.push(expr);
|
||||
}
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
struct IrMaps<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
live_node_map: HirIdMap<LiveNode>,
|
||||
@ -1333,7 +1346,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
||||
self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
|
||||
self.check_unused_vars_in_pat(&local.pat, None, None, |spans, hir_id, ln, var| {
|
||||
if local.init.is_some() {
|
||||
self.warn_about_dead_assign(spans, hir_id, ln, var);
|
||||
}
|
||||
@ -1348,7 +1361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
||||
self.check_unused_vars_in_pat(&arm.pat, None, |_, _, _, _| {});
|
||||
self.check_unused_vars_in_pat(&arm.pat, None, None, |_, _, _, _| {});
|
||||
intravisit::walk_arm(self, arm);
|
||||
}
|
||||
}
|
||||
@ -1387,7 +1400,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(let_expr) => {
|
||||
this.check_unused_vars_in_pat(let_expr.pat, None, |_, _, _, _| {});
|
||||
this.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {});
|
||||
}
|
||||
|
||||
// no correctness conditions related to liveness
|
||||
@ -1508,13 +1521,18 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
|
||||
fn warn_about_unused_args(&self, body: &hir::Body<'_>, entry_ln: LiveNode) {
|
||||
for p in body.params {
|
||||
self.check_unused_vars_in_pat(&p.pat, Some(entry_ln), |spans, hir_id, ln, var| {
|
||||
if !self.live_on_entry(ln, var) {
|
||||
self.report_unused_assign(hir_id, spans, var, |name| {
|
||||
format!("value passed to `{}` is never read", name)
|
||||
});
|
||||
}
|
||||
});
|
||||
self.check_unused_vars_in_pat(
|
||||
&p.pat,
|
||||
Some(entry_ln),
|
||||
Some(body),
|
||||
|spans, hir_id, ln, var| {
|
||||
if !self.live_on_entry(ln, var) {
|
||||
self.report_unused_assign(hir_id, spans, var, |name| {
|
||||
format!("value passed to `{}` is never read", name)
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1522,6 +1540,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
&self,
|
||||
pat: &hir::Pat<'_>,
|
||||
entry_ln: Option<LiveNode>,
|
||||
opt_body: Option<&hir::Body<'_>>,
|
||||
on_used_on_entry: impl Fn(Vec<Span>, HirId, LiveNode, Variable),
|
||||
) {
|
||||
// In an or-pattern, only consider the variable; any later patterns must have the same
|
||||
@ -1549,7 +1568,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
hir_ids_and_spans.into_iter().map(|(_, _, ident_span)| ident_span).collect();
|
||||
on_used_on_entry(spans, id, ln, var);
|
||||
} else {
|
||||
self.report_unused(hir_ids_and_spans, ln, var, can_remove);
|
||||
self.report_unused(hir_ids_and_spans, ln, var, can_remove, pat, opt_body);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1561,6 +1580,8 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
ln: LiveNode,
|
||||
var: Variable,
|
||||
can_remove: bool,
|
||||
pat: &hir::Pat<'_>,
|
||||
opt_body: Option<&hir::Body<'_>>,
|
||||
) {
|
||||
let first_hir_id = hir_ids_and_spans[0].0;
|
||||
|
||||
@ -1664,6 +1685,9 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
.collect::<Vec<_>>(),
|
||||
|lint| {
|
||||
let mut err = lint.build(&format!("unused variable: `{}`", name));
|
||||
if self.has_added_lit_match_name_span(&name, opt_body, &mut err) {
|
||||
err.span_label(pat.span, "unused variable");
|
||||
}
|
||||
err.multipart_suggestion(
|
||||
"if this is intentional, prefix it with an underscore",
|
||||
non_shorthands,
|
||||
@ -1677,6 +1701,42 @@ impl<'tcx> Liveness<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn has_added_lit_match_name_span(
|
||||
&self,
|
||||
name: &str,
|
||||
opt_body: Option<&hir::Body<'_>>,
|
||||
err: &mut rustc_errors::DiagnosticBuilder<'_, ()>,
|
||||
) -> bool {
|
||||
let mut has_litstring = false;
|
||||
let Some(opt_body) = opt_body else {return false;};
|
||||
let mut visitor = CollectLitsVisitor { lit_exprs: vec![] };
|
||||
intravisit::walk_body(&mut visitor, opt_body);
|
||||
for lit_expr in visitor.lit_exprs {
|
||||
let hir::ExprKind::Lit(litx) = &lit_expr.kind else { continue };
|
||||
let rustc_ast::LitKind::Str(syb, _) = litx.node else{ continue; };
|
||||
let name_str: &str = syb.as_str();
|
||||
let mut name_pa = String::from("{");
|
||||
name_pa.push_str(&name);
|
||||
name_pa.push('}');
|
||||
if name_str.contains(&name_pa) {
|
||||
err.span_label(
|
||||
lit_expr.span,
|
||||
"you might have meant to use string interpolation in this string literal",
|
||||
);
|
||||
err.multipart_suggestion(
|
||||
"string interpolation only works in `format!` invocations",
|
||||
vec![
|
||||
(lit_expr.span.shrink_to_lo(), "format!(".to_string()),
|
||||
(lit_expr.span.shrink_to_hi(), ")".to_string()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
has_litstring = true;
|
||||
}
|
||||
}
|
||||
has_litstring
|
||||
}
|
||||
|
||||
fn warn_about_dead_assign(&self, spans: Vec<Span>, hir_id: HirId, ln: LiveNode, var: Variable) {
|
||||
if !self.live_on_exit(ln, var) {
|
||||
self.report_unused_assign(hir_id, spans, var, |name| {
|
||||
|
@ -17,6 +17,7 @@ use rustc_span::Span;
|
||||
|
||||
pub trait TraitEngineExt<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
|
||||
fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
|
||||
}
|
||||
|
||||
impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
|
||||
@ -27,6 +28,14 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
|
||||
Box::new(FulfillmentContext::new())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
|
||||
if tcx.sess.opts.unstable_opts.chalk {
|
||||
Box::new(ChalkFulfillmentContext::new())
|
||||
} else {
|
||||
Box::new(FulfillmentContext::new_in_snapshot())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used if you want to have pleasant experience when dealing
|
||||
@ -41,6 +50,10 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
|
||||
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
|
||||
}
|
||||
|
||||
pub fn new_in_snapshot(infcx: &'a InferCtxt<'a, 'tcx>) -> Self {
|
||||
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
|
||||
}
|
||||
|
||||
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
|
||||
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::hir::map;
|
||||
use rustc_middle::ty::{
|
||||
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
|
||||
@ -1589,32 +1589,38 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
expected: ty::PolyTraitRef<'tcx>,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
pub(crate) fn build_fn_sig_ty<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
infcx: &InferCtxt<'_, 'tcx>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let inputs = trait_ref.skip_binder().substs.type_at(1);
|
||||
let sig = match inputs.kind() {
|
||||
ty::Tuple(inputs)
|
||||
if tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() =>
|
||||
if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() =>
|
||||
{
|
||||
tcx.mk_fn_sig(
|
||||
infcx.tcx.mk_fn_sig(
|
||||
inputs.iter(),
|
||||
tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))),
|
||||
infcx.next_ty_var(TypeVariableOrigin {
|
||||
span: DUMMY_SP,
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
}),
|
||||
false,
|
||||
hir::Unsafety::Normal,
|
||||
abi::Abi::Rust,
|
||||
)
|
||||
}
|
||||
_ => tcx.mk_fn_sig(
|
||||
_ => infcx.tcx.mk_fn_sig(
|
||||
std::iter::once(inputs),
|
||||
tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))),
|
||||
infcx.next_ty_var(TypeVariableOrigin {
|
||||
span: DUMMY_SP,
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
}),
|
||||
false,
|
||||
hir::Unsafety::Normal,
|
||||
abi::Abi::Rust,
|
||||
),
|
||||
};
|
||||
|
||||
tcx.mk_fn_ptr(trait_ref.rebind(sig))
|
||||
infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig))
|
||||
}
|
||||
|
||||
let argument_kind = match expected.skip_binder().self_ty().kind() {
|
||||
@ -1634,11 +1640,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
let found_span = found_span.unwrap_or(span);
|
||||
err.span_label(found_span, "found signature defined here");
|
||||
|
||||
let expected = build_fn_sig_ty(self.tcx, expected);
|
||||
let found = build_fn_sig_ty(self.tcx, found);
|
||||
let expected = build_fn_sig_ty(self, expected);
|
||||
let found = build_fn_sig_ty(self, found);
|
||||
|
||||
let (expected_str, found_str) =
|
||||
self.tcx.infer_ctxt().enter(|infcx| infcx.cmp(expected, found));
|
||||
let (expected_str, found_str) = self.cmp(expected, found);
|
||||
|
||||
let signature_kind = format!("{argument_kind} signature");
|
||||
err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use super::callee::DeferredCallResolution;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::HirIdMap;
|
||||
@ -12,7 +13,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_span::def_id::LocalDefIdMap;
|
||||
use rustc_span::{self, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{self, ObligationCause, TraitEngine, TraitEngineExt};
|
||||
use rustc_trait_selection::traits::{
|
||||
self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _,
|
||||
};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
@ -84,7 +87,29 @@ impl<'tcx> Inherited<'_, 'tcx> {
|
||||
infcx: tcx
|
||||
.infer_ctxt()
|
||||
.ignoring_regions()
|
||||
.with_fresh_in_progress_typeck_results(hir_owner),
|
||||
.with_fresh_in_progress_typeck_results(hir_owner)
|
||||
.with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| {
|
||||
if fn_sig.has_escaping_bound_vars() {
|
||||
return fn_sig;
|
||||
}
|
||||
infcx.probe(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(infcx);
|
||||
let normalized_fn_sig = ocx.normalize(
|
||||
ObligationCause::dummy(),
|
||||
// FIXME(compiler-errors): This is probably not the right param-env...
|
||||
infcx.tcx.param_env(def_id),
|
||||
fn_sig,
|
||||
);
|
||||
if ocx.select_all_or_error().is_empty() {
|
||||
let normalized_fn_sig =
|
||||
infcx.resolve_vars_if_possible(normalized_fn_sig);
|
||||
if !normalized_fn_sig.needs_infer() {
|
||||
return normalized_fn_sig;
|
||||
}
|
||||
}
|
||||
fn_sig
|
||||
})
|
||||
})),
|
||||
def_id,
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/type-check-1.rs:60:26
|
||||
|
|
||||
LL | asm!("{}", const 0 as *mut u8);
|
||||
| ^^^^^^^^^^^^ expected integer, found *-ptr
|
||||
| ^^^^^^^^^^^^ expected integer, found `*mut u8`
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found raw pointer `*mut u8`
|
||||
@ -133,7 +133,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/type-check-1.rs:78:25
|
||||
|
|
||||
LL | global_asm!("{}", const 0 as *mut u8);
|
||||
| ^^^^^^^^^^^^ expected integer, found *-ptr
|
||||
| ^^^^^^^^^^^^ expected integer, found `*mut u8`
|
||||
|
|
||||
= note: expected type `{integer}`
|
||||
found raw pointer `*mut u8`
|
||||
|
@ -2,7 +2,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/dst-bad-coercions.rs:14:17
|
||||
|
|
||||
LL | let y: &S = x;
|
||||
| -- ^ expected `&S`, found *-ptr
|
||||
| -- ^ expected `&S`, found `*const S`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
@ -13,7 +13,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/dst-bad-coercions.rs:15:21
|
||||
|
|
||||
LL | let y: &dyn T = x;
|
||||
| ------ ^ expected `&dyn T`, found *-ptr
|
||||
| ------ ^ expected `&dyn T`, found `*const S`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
@ -24,7 +24,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/dst-bad-coercions.rs:19:17
|
||||
|
|
||||
LL | let y: &S = x;
|
||||
| -- ^ expected `&S`, found *-ptr
|
||||
| -- ^ expected `&S`, found `*mut S`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
@ -35,7 +35,7 @@ error[E0308]: mismatched types
|
||||
--> $DIR/dst-bad-coercions.rs:20:21
|
||||
|
|
||||
LL | let y: &dyn T = x;
|
||||
| ------ ^ expected `&dyn T`, found *-ptr
|
||||
| ------ ^ expected `&dyn T`, found `*mut S`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
|
13
src/test/ui/impl-trait/issue-99914.rs
Normal file
13
src/test/ui/impl-trait/issue-99914.rs
Normal file
@ -0,0 +1,13 @@
|
||||
// edition:2021
|
||||
|
||||
fn main() {}
|
||||
|
||||
struct Error;
|
||||
struct Okay;
|
||||
|
||||
fn foo(t: Result<Okay, Error>) {
|
||||
t.and_then(|t| -> _ { bar(t) });
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
|
||||
async fn bar(t: Okay) {}
|
21
src/test/ui/impl-trait/issue-99914.stderr
Normal file
21
src/test/ui/impl-trait/issue-99914.stderr
Normal file
@ -0,0 +1,21 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-99914.rs:9:27
|
||||
|
|
||||
LL | t.and_then(|t| -> _ { bar(t) });
|
||||
| ^^^^^^ expected enum `Result`, found opaque type
|
||||
|
|
||||
note: while checking the return type of the `async fn`
|
||||
--> $DIR/issue-99914.rs:13:23
|
||||
|
|
||||
LL | async fn bar(t: Okay) {}
|
||||
| ^ checked the `Output` of this `async fn`, found opaque type
|
||||
= note: expected enum `Result<_, Error>`
|
||||
found opaque type `impl Future<Output = ()>`
|
||||
help: try wrapping the expression in `Ok`
|
||||
|
|
||||
LL | t.and_then(|t| -> _ { Ok(bar(t)) });
|
||||
| +++ +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -4,7 +4,7 @@ error[E0308]: mismatched types
|
||||
LL | fn function(t: &mut dyn Trait) {
|
||||
| - help: try adding a return type: `-> *mut dyn Trait`
|
||||
LL | t as *mut dyn Trait
|
||||
| ^^^^^^^^^^^^^^^^^^^ expected `()`, found *-ptr
|
||||
| ^^^^^^^^^^^^^^^^^^^ expected `()`, found `*mut dyn Trait`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found raw pointer `*mut dyn Trait`
|
||||
|
16
src/test/ui/mismatched_types/normalize-fn-sig.rs
Normal file
16
src/test/ui/mismatched_types/normalize-fn-sig.rs
Normal file
@ -0,0 +1,16 @@
|
||||
trait Foo {
|
||||
type Bar;
|
||||
}
|
||||
|
||||
impl<T> Foo for T {
|
||||
type Bar = i32;
|
||||
}
|
||||
|
||||
fn foo<T>(_: <T as Foo>::Bar, _: &'static <T as Foo>::Bar) {}
|
||||
|
||||
fn needs_i32_ref_fn(_: fn(&'static i32, i32)) {}
|
||||
|
||||
fn main() {
|
||||
needs_i32_ref_fn(foo::<()>);
|
||||
//~^ ERROR mismatched types
|
||||
}
|
19
src/test/ui/mismatched_types/normalize-fn-sig.stderr
Normal file
19
src/test/ui/mismatched_types/normalize-fn-sig.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/normalize-fn-sig.rs:14:22
|
||||
|
|
||||
LL | needs_i32_ref_fn(foo::<()>);
|
||||
| ---------------- ^^^^^^^^^ expected `&i32`, found `i32`
|
||||
| |
|
||||
| arguments to this function are incorrect
|
||||
|
|
||||
= note: expected fn pointer `fn(&'static i32, i32)`
|
||||
found fn item `fn(i32, &'static i32) {foo::<()>}`
|
||||
note: function defined here
|
||||
--> $DIR/normalize-fn-sig.rs:11:4
|
||||
|
|
||||
LL | fn needs_i32_ref_fn(_: fn(&'static i32, i32)) {}
|
||||
| ^^^^^^^^^^^^^^^^ ------------------------
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
15
src/test/ui/type/issue-100584.rs
Normal file
15
src/test/ui/type/issue-100584.rs
Normal file
@ -0,0 +1,15 @@
|
||||
#![deny(unused)]
|
||||
fn foo(xyza: &str) {
|
||||
//~^ ERROR unused variable: `xyza`
|
||||
let _ = "{xyza}";
|
||||
}
|
||||
|
||||
fn foo3(xyza: &str) {
|
||||
//~^ ERROR unused variable: `xyza`
|
||||
let _ = "aaa{xyza}bbb";
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo("x");
|
||||
foo3("xx");
|
||||
}
|
44
src/test/ui/type/issue-100584.stderr
Normal file
44
src/test/ui/type/issue-100584.stderr
Normal file
@ -0,0 +1,44 @@
|
||||
error: unused variable: `xyza`
|
||||
--> $DIR/issue-100584.rs:2:8
|
||||
|
|
||||
LL | fn foo(xyza: &str) {
|
||||
| ^^^^ unused variable
|
||||
LL |
|
||||
LL | let _ = "{xyza}";
|
||||
| -------- you might have meant to use string interpolation in this string literal
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/issue-100584.rs:1:9
|
||||
|
|
||||
LL | #![deny(unused)]
|
||||
| ^^^^^^
|
||||
= note: `#[deny(unused_variables)]` implied by `#[deny(unused)]`
|
||||
help: string interpolation only works in `format!` invocations
|
||||
|
|
||||
LL | let _ = format!("{xyza}");
|
||||
| ++++++++ +
|
||||
help: if this is intentional, prefix it with an underscore
|
||||
|
|
||||
LL | fn foo(_xyza: &str) {
|
||||
| ~~~~~
|
||||
|
||||
error: unused variable: `xyza`
|
||||
--> $DIR/issue-100584.rs:7:9
|
||||
|
|
||||
LL | fn foo3(xyza: &str) {
|
||||
| ^^^^ unused variable
|
||||
LL |
|
||||
LL | let _ = "aaa{xyza}bbb";
|
||||
| -------------- you might have meant to use string interpolation in this string literal
|
||||
|
|
||||
help: string interpolation only works in `format!` invocations
|
||||
|
|
||||
LL | let _ = format!("aaa{xyza}bbb");
|
||||
| ++++++++ +
|
||||
help: if this is intentional, prefix it with an underscore
|
||||
|
|
||||
LL | fn foo3(_xyza: &str) {
|
||||
| ~~~~~
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user