1
Fork 0

Auto merge of #110137 - Dylan-DPC:rollup-fdruvwp, r=Dylan-DPC

Rollup of 6 pull requests

Successful merges:

 - #109724 (prioritize param env candidates if they don't guide type inference)
 - #110021 (Fix a couple ICEs in the new `CastKind::Transmute` code)
 - #110044 (Avoid some manual slice length calculation)
 - #110115 (compiletest: Use remap-path-prefix only in CI)
 - #110121 (Fix `x check --stage 1` when download-rustc is enabled)
 - #110124 (Some clippy fixes in the compiler)

Failed merges:

 - #109752 (Stall auto trait assembly in new solver for int/float vars)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-04-10 09:07:02 +00:00
commit d4be8efc62
76 changed files with 631 additions and 302 deletions

View file

@ -1176,7 +1176,7 @@ impl FieldsShape {
/// Gets source indices of the fields by increasing offsets.
#[inline]
pub fn index_by_increasing_offset<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
pub fn index_by_increasing_offset(&self) -> impl Iterator<Item = usize> + '_ {
let mut inverse_small = [0u8; 64];
let mut inverse_big = IndexVec::new();
let use_small = self.count() <= inverse_small.len();

View file

@ -22,6 +22,7 @@
#![feature(strict_provenance)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#![allow(clippy::mut_from_ref)] // Arena allocators are one of the places where this pattern is fine.
use smallvec::SmallVec;
@ -568,7 +569,9 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
}
pub trait ArenaAllocatable<'tcx, C = rustc_arena::IsNotCopy>: Sized {
#[allow(clippy::mut_from_ref)]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self;
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
@ -578,10 +581,12 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
// Any type that impls `Copy` can be arena-allocated in the `DroplessArena`.
impl<'tcx, T: Copy> ArenaAllocatable<'tcx, rustc_arena::IsCopy> for T {
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_on<'a>(self, arena: &'a Arena<'tcx>) -> &'a mut Self {
arena.dropless.alloc(self)
}
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
@ -601,6 +606,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
}
#[inline]
#[allow(clippy::mut_from_ref)]
fn allocate_from_iter<'a>(
arena: &'a Arena<'tcx>,
iter: impl ::std::iter::IntoIterator<Item = Self>,
@ -616,12 +622,14 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
impl<'tcx> Arena<'tcx> {
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc<T: ArenaAllocatable<'tcx, C>, C>(&self, value: T) -> &mut T {
value.allocate_on(self)
}
// Any type that impls `Copy` can have slices be arena-allocated in the `DroplessArena`.
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn alloc_slice<T: ::std::marker::Copy>(&self, value: &[T]) -> &mut [T] {
if value.is_empty() {
return &mut [];
@ -629,6 +637,7 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
self.dropless.alloc_slice(value)
}
#[allow(clippy::mut_from_ref)]
pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, C>, C>(
&'a self,
iter: impl ::std::iter::IntoIterator<Item = T>,

View file

@ -691,7 +691,7 @@ fn validate_generic_param_order(
GenericParamKind::Lifetime => (),
GenericParamKind::Const { ty: _, kw_span: _, default: Some(default) } => {
ordered_params += " = ";
ordered_params += &pprust::expr_to_string(&*default.value);
ordered_params += &pprust::expr_to_string(&default.value);
}
GenericParamKind::Const { ty: _, kw_span: _, default: None } => (),
}

View file

@ -404,11 +404,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
);
} else {
// And if it isn't, cancel the early-pass warning.
self.sess
if let Some(err) = self
.sess
.parse_sess
.span_diagnostic
.steal_diagnostic(e.span, StashKey::EarlySyntaxWarning)
.map(|err| err.cancel());
{
err.cancel()
}
}
}
ast::ExprKind::TryBlock(_) => {

View file

@ -686,7 +686,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
let has_comment = self.maybe_print_comment(span.hi());
if !empty || has_comment {
self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
self.break_offset_if_not_bol(1, -INDENT_UNIT);
}
self.word("}");
if close_box {
@ -988,7 +988,9 @@ impl<'a> State<'a> {
pub fn print_assoc_constraint(&mut self, constraint: &ast::AssocConstraint) {
self.print_ident(constraint.ident);
constraint.gen_args.as_ref().map(|args| self.print_generic_args(args, false));
if let Some(args) = constraint.gen_args.as_ref() {
self.print_generic_args(args, false)
}
self.space();
match &constraint.kind {
ast::AssocConstraintKind::Equality { term } => {

View file

@ -259,6 +259,31 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
}
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
/// Returns an `OperandValue` that's generally UB to use in any way.
///
/// Depending on the `layout`, returns an `Immediate` or `Pair` containing
/// poison value(s), or a `Ref` containing a poison pointer.
///
/// Supports sized types only.
pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
bx: &mut Bx,
layout: TyAndLayout<'tcx>,
) -> OperandValue<V> {
assert!(layout.is_sized());
if bx.cx().is_backend_immediate(layout) {
let ibty = bx.cx().immediate_backend_type(layout);
OperandValue::Immediate(bx.const_poison(ibty))
} else if bx.cx().is_backend_scalar_pair(layout) {
let ibty0 = bx.cx().scalar_pair_element_backend_type(layout, 0, true);
let ibty1 = bx.cx().scalar_pair_element_backend_type(layout, 1, true);
OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1))
} else {
let bty = bx.cx().backend_type(layout);
let ptr_bty = bx.cx().type_ptr_to(bty);
OperandValue::Ref(bx.const_poison(ptr_bty), None, layout.align.abi)
}
}
pub fn store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
self,
bx: &mut Bx,

View file

@ -158,17 +158,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
debug_assert!(src.layout.is_sized());
debug_assert!(dst.layout.is_sized());
if src.layout.size != dst.layout.size
|| src.layout.abi.is_uninhabited()
|| dst.layout.abi.is_uninhabited()
{
// In all of these cases it's UB to run this transmute, but that's
// known statically so might as well trap for it, rather than just
// making it unreachable.
bx.abort();
return;
}
if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) {
val.store(bx, dst);
return;
@ -202,8 +191,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
operand: OperandRef<'tcx, Bx::Value>,
cast: TyAndLayout<'tcx>,
) -> Option<OperandValue<Bx::Value>> {
// Callers already checked that the layout sizes match
debug_assert_eq!(operand.layout.size, cast.size);
// Check for transmutes that are always UB.
if operand.layout.size != cast.size
|| operand.layout.abi.is_uninhabited()
|| cast.abi.is_uninhabited()
{
if !operand.layout.abi.is_uninhabited() {
// Since this is known statically and the input could have existed
// without already having hit UB, might as well trap for it.
bx.abort();
}
// Because this transmute is UB, return something easy to generate,
// since it's fine that later uses of the value are probably UB.
return Some(OperandValue::poison(bx, cast));
}
let operand_kind = self.value_kind(operand.layout);
let cast_kind = self.value_kind(cast);
@ -222,10 +224,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bug!("Found {operand_kind:?} for operand {operand:?}");
};
if let OperandValueKind::Immediate(out_scalar) = cast_kind {
let cast_bty = bx.backend_type(cast);
Some(OperandValue::Immediate(Self::transmute_immediate(
bx, imm, in_scalar, out_scalar, cast_bty,
)))
match (in_scalar, out_scalar) {
(ScalarOrZst::Zst, ScalarOrZst::Zst) => {
Some(OperandRef::new_zst(bx, cast).val)
}
(ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar))
if in_scalar.size(self.cx) == out_scalar.size(self.cx) =>
{
let cast_bty = bx.backend_type(cast);
Some(OperandValue::Immediate(
self.transmute_immediate(bx, imm, in_scalar, out_scalar, cast_bty),
))
}
_ => None,
}
} else {
None
}
@ -234,12 +246,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let OperandValueKind::Pair(in_a, in_b) = operand_kind else {
bug!("Found {operand_kind:?} for operand {operand:?}");
};
if let OperandValueKind::Pair(out_a, out_b) = cast_kind {
if let OperandValueKind::Pair(out_a, out_b) = cast_kind
&& in_a.size(self.cx) == out_a.size(self.cx)
&& in_b.size(self.cx) == out_b.size(self.cx)
{
let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
Some(OperandValue::Pair(
Self::transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
Self::transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
self.transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
self.transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
))
} else {
None
@ -254,12 +269,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
/// `to_backend_ty` must be the *non*-immediate backend type (so it will be
/// `i8`, not `i1`, for `bool`-like types.)
fn transmute_immediate(
&self,
bx: &mut Bx,
mut imm: Bx::Value,
from_scalar: abi::Scalar,
to_scalar: abi::Scalar,
to_backend_ty: Bx::Type,
) -> Bx::Value {
debug_assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
use abi::Primitive::*;
imm = bx.from_immediate(imm);
imm = match (from_scalar.primitive(), to_scalar.primitive()) {
@ -831,14 +849,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let operand_ty = operand.ty(self.mir, self.cx.tcx());
let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
if operand_layout.size != cast_layout.size
|| operand_layout.abi.is_uninhabited()
|| cast_layout.abi.is_uninhabited()
{
// Send UB cases to the full form so the operand version can
// `bitcast` without worrying about malformed IR.
return false;
}
match (self.value_kind(operand_layout), self.value_kind(cast_layout)) {
// Can always load from a pointer as needed
@ -847,9 +857,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// Need to generate an `alloc` to get a pointer from an immediate
(OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
// When we have scalar immediates, we can convert them as needed
(OperandValueKind::Immediate(..), OperandValueKind::Immediate(..)) |
(OperandValueKind::Pair(..), OperandValueKind::Pair(..)) => true,
// When we have scalar immediates, we can only convert things
// where the sizes match, to avoid endianness questions.
(OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) =>
a.size(self.cx) == b.size(self.cx),
(OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) =>
a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
// Send mixings between scalars and pairs through the memory route
// FIXME: Maybe this could use insertvalue/extractvalue instead?
@ -887,13 +900,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if self.cx.is_backend_immediate(layout) {
debug_assert!(!self.cx.is_backend_scalar_pair(layout));
OperandValueKind::Immediate(match layout.abi {
abi::Abi::Scalar(s) => s,
abi::Abi::Vector { element, .. } => element,
x => bug!("Couldn't translate {x:?} as backend immediate"),
abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s),
abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element),
_ if layout.is_zst() => ScalarOrZst::Zst,
x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
})
} else if self.cx.is_backend_scalar_pair(layout) {
let abi::Abi::ScalarPair(s1, s2) = layout.abi else {
bug!("Couldn't translate {:?} as backend scalar pair", layout.abi)
span_bug!(
self.mir.span,
"Couldn't translate {:?} as backend scalar pair",
layout.abi,
);
};
OperandValueKind::Pair(s1, s2)
} else {
@ -902,9 +920,26 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
/// The variants of this match [`OperandValue`], giving details about the
/// backend values that will be held in that other type.
#[derive(Debug, Copy, Clone)]
enum OperandValueKind {
Ref,
Immediate(abi::Scalar),
Immediate(ScalarOrZst),
Pair(abi::Scalar, abi::Scalar),
}
#[derive(Debug, Copy, Clone)]
enum ScalarOrZst {
Zst,
Scalar(abi::Scalar),
}
impl ScalarOrZst {
pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size {
match self {
ScalarOrZst::Zst => abi::Size::ZERO,
ScalarOrZst::Scalar(s) => s.size(cx),
}
}
}

View file

@ -206,17 +206,11 @@ impl<N: Debug, E: Debug> Graph<N, E> {
AdjacentEdges { graph: self, direction, next: first_edge }
}
pub fn successor_nodes<'a>(
&'a self,
source: NodeIndex,
) -> impl Iterator<Item = NodeIndex> + 'a {
pub fn successor_nodes(&self, source: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
self.outgoing_edges(source).targets()
}
pub fn predecessor_nodes<'a>(
&'a self,
target: NodeIndex,
) -> impl Iterator<Item = NodeIndex> + 'a {
pub fn predecessor_nodes(&self, target: NodeIndex) -> impl Iterator<Item = NodeIndex> + '_ {
self.incoming_edges(target).sources()
}

View file

@ -40,7 +40,7 @@ impl Deref for Mmap {
impl AsRef<[u8]> for Mmap {
fn as_ref(&self) -> &[u8] {
&*self.0
&self.0
}
}

View file

@ -778,7 +778,7 @@ pub fn print_time_passes_entry(
"rss_start": start_rss,
"rss_end": end_rss,
});
eprintln!("time: {}", json.to_string());
eprintln!("time: {json}");
return;
}
TimePassesFormat::Text => (),

View file

@ -140,6 +140,7 @@ pub fn make_hash<K: Hash + ?Sized>(val: &K) -> u64 {
/// `hash` can be computed with any hasher, so long as that hasher is used
/// consistently for each `Sharded` instance.
#[inline]
#[allow(clippy::modulo_one)]
pub fn get_shard_index_by_hash(hash: u64) -> usize {
let hash_len = mem::size_of::<usize>();
// Ignore the top 7 bits as hashbrown uses these and get the next SHARD_BITS highest bits.

View file

@ -312,14 +312,14 @@ impl<CTX> HashStable<CTX> for ::std::num::NonZeroUsize {
impl<CTX> HashStable<CTX> for f32 {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let val: u32 = unsafe { ::std::mem::transmute(*self) };
let val: u32 = self.to_bits();
val.hash_stable(ctx, hasher);
}
}
impl<CTX> HashStable<CTX> for f64 {
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
let val: u64 = unsafe { ::std::mem::transmute(*self) };
let val: u64 = self.to_bits();
val.hash_stable(ctx, hasher);
}
}

View file

@ -5,7 +5,7 @@ const RED_ZONE: usize = 100 * 1024; // 100k
// Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then
// on. This flag has performance relevant characteristics. Don't set it too high.
const STACK_PER_RECURSION: usize = 1 * 1024 * 1024; // 1MB
const STACK_PER_RECURSION: usize = 1024 * 1024; // 1MB
/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations
/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit

View file

@ -84,7 +84,7 @@ impl<T: Copy> AppendOnlyVec<T> {
}
pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
(0..).map(|i| self.get(i)).take_while(|o| o.is_some()).filter_map(|o| o)
(0..).map(|i| self.get(i)).take_while(|o| o.is_some()).flatten()
}
}

View file

@ -224,7 +224,7 @@ impl<V: Eq + Hash> UnordSet<V> {
}
#[inline]
pub fn items<'a>(&'a self) -> UnordItems<&'a V, impl Iterator<Item = &'a V>> {
pub fn items(&self) -> UnordItems<&V, impl Iterator<Item = &V>> {
UnordItems(self.inner.iter())
}
@ -415,7 +415,7 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
}
#[inline]
pub fn items<'a>(&'a self) -> UnordItems<(&'a K, &'a V), impl Iterator<Item = (&'a K, &'a V)>> {
pub fn items(&self) -> UnordItems<(&K, &V), impl Iterator<Item = (&K, &V)>> {
UnordItems(self.inner.iter())
}

View file

@ -956,7 +956,7 @@ impl Diagnostic {
// Exact iteration order of diagnostic arguments shouldn't make a difference to output because
// they're only used in interpolation.
#[allow(rustc::potential_query_instability)]
pub fn args<'a>(&'a self) -> impl Iterator<Item = DiagnosticArg<'a, 'static>> {
pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_, 'static>> {
self.args.iter()
}

View file

@ -1407,7 +1407,7 @@ impl EmitterWriter {
// Account for newlines to align output to its label.
for (line, text) in normalize_whitespace(&text).lines().enumerate() {
buffer.append(
0 + line,
line,
&format!(
"{}{}",
if line == 0 { String::new() } else { " ".repeat(label_width) },
@ -1918,7 +1918,7 @@ impl EmitterWriter {
let last_line = unhighlighted_lines.pop();
let first_line = unhighlighted_lines.drain(..).next();
first_line.map(|(p, l)| {
if let Some((p, l)) = first_line {
self.draw_code_line(
&mut buffer,
&mut row_num,
@ -1930,12 +1930,12 @@ impl EmitterWriter {
&file_lines,
is_multiline,
)
});
}
buffer.puts(row_num, max_line_num_len - 1, "...", Style::LineNumber);
row_num += 1;
last_line.map(|(p, l)| {
if let Some((p, l)) = last_line {
self.draw_code_line(
&mut buffer,
&mut row_num,
@ -1947,7 +1947,7 @@ impl EmitterWriter {
&file_lines,
is_multiline,
)
});
}
}
}

View file

@ -466,7 +466,7 @@ impl<'a> StripUnconfigured<'a> {
//
// N.B., this is intentionally not part of the visit_expr() function
// in order for filter_map_expr() to be able to avoid this check
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) {
if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
self.sess.emit_err(RemoveExprNotSupported { span: attr.span });
}

View file

@ -41,7 +41,7 @@ impl MetaVarExpr {
};
check_trailing_token(&mut tts, sess)?;
let mut iter = args.trees();
let rslt = match &*ident.as_str() {
let rslt = match ident.as_str() {
"count" => parse_count(&mut iter, sess, ident.span)?,
"ignore" => MetaVarExpr::Ignore(parse_ident(&mut iter, sess, ident.span)?),
"index" => MetaVarExpr::Index(parse_depth(&mut iter, sess, ident.span)?),

View file

@ -49,7 +49,7 @@ impl LanguageItems {
self.get(it).ok_or_else(|| LangItemError(it))
}
pub fn iter<'a>(&'a self) -> impl Iterator<Item = (LangItem, DefId)> + 'a {
pub fn iter(&self) -> impl Iterator<Item = (LangItem, DefId)> + '_ {
self.items
.iter()
.enumerate()

View file

@ -302,7 +302,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
.iter()
.flatten()
.map(|r| r.impl_blocks.len() as isize - avg as isize)
.map(|v| v.abs() as usize)
.map(|v| v.unsigned_abs())
.sum::<usize>();
s / connected_regions.len()
},

View file

@ -242,7 +242,7 @@ pub fn enum_def_to_string(
impl<'a> State<'a> {
pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
self.maybe_print_comment(span.hi());
self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
self.break_offset_if_not_bol(1, -INDENT_UNIT);
self.word("}");
if close_box {
self.end(); // close the outer-box

View file

@ -538,8 +538,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME(rpitit): This will need to be fixed when we move to associated types
assert!(matches!(
*trait_pred.trait_ref.self_ty().kind(),
ty::Alias(_, ty::AliasTy { def_id, substs, .. })
if def_id == rpit_def_id && substs == substs
ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. })
if def_id == rpit_def_id && substs == alias_substs
));
ty::PredicateKind::Clause(ty::Clause::Trait(
trait_pred.with_self_ty(self.tcx, ty),
@ -548,8 +548,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => {
assert!(matches!(
*proj_pred.projection_ty.self_ty().kind(),
ty::Alias(_, ty::AliasTy { def_id, substs, .. })
if def_id == rpit_def_id && substs == substs
ty::Alias(_, ty::AliasTy { def_id, substs: alias_substs, .. })
if def_id == rpit_def_id && substs == alias_substs
));
proj_pred = proj_pred.with_self_ty(self.tcx, ty);
ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred))

View file

@ -242,8 +242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let Some(arg) = segment
.args()
.args
.iter()
.nth(index) else { return false; };
.get(index) else { return false; };
error.obligation.cause.span = arg
.span()
.find_ancestor_in_same_ctxt(error.obligation.cause.span)

View file

@ -526,8 +526,9 @@ impl DropRangesBuilder {
let mut next = <_>::from(0u32);
for value in tracked_values {
for_each_consumable(hir, value, |value| {
if !tracked_value_map.contains_key(&value) {
tracked_value_map.insert(value, next);
if let std::collections::hash_map::Entry::Vacant(e) = tracked_value_map.entry(value)
{
e.insert(next);
next = next + 1;
}
});

View file

@ -313,8 +313,7 @@ pub fn resolve_interior<'a, 'tcx>(
// Extract type components to build the witness type.
let type_list = fcx.tcx.mk_type_list_from_iter(type_causes.iter().map(|cause| cause.ty));
let bound_vars = fcx.tcx.mk_bound_variable_kinds(&bound_vars);
let witness =
fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone()));
let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars));
drop(typeck_results);
// Store the generator types and spans into the typeck results for this generator.

View file

@ -1764,7 +1764,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
debug!("probing for method names similar to {:?}", self.method_name);
let steps = self.steps.clone();
self.probe(|_| {
let mut pcx = ProbeContext::new(
self.fcx,
@ -1772,8 +1771,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
self.mode,
self.method_name,
self.return_type,
&self.orig_steps_var_values,
steps,
self.orig_steps_var_values,
self.steps,
self.scope_expr_id,
);
pcx.allow_similar_names = true;

View file

@ -1850,7 +1850,7 @@ impl<R: Idx, C: Idx> SparseBitMatrix<R, C> {
/// Iterates through all the columns set to true in a given row of
/// the matrix.
pub fn iter<'a>(&'a self, row: R) -> impl Iterator<Item = C> + 'a {
pub fn iter(&self, row: R) -> impl Iterator<Item = C> + '_ {
self.row(row).into_iter().flat_map(|r| r.iter())
}

View file

@ -201,18 +201,15 @@ impl<I: Idx, T> IndexVec<I, T> {
}
#[inline]
pub fn drain<'a, R: RangeBounds<usize>>(
&'a mut self,
range: R,
) -> impl Iterator<Item = T> + 'a {
pub fn drain<R: RangeBounds<usize>>(&mut self, range: R) -> impl Iterator<Item = T> + '_ {
self.raw.drain(range)
}
#[inline]
pub fn drain_enumerated<'a, R: RangeBounds<usize>>(
&'a mut self,
pub fn drain_enumerated<R: RangeBounds<usize>>(
&mut self,
range: R,
) -> impl Iterator<Item = (I, T)> + 'a {
) -> impl Iterator<Item = (I, T)> + '_ {
let begin = match range.start_bound() {
std::ops::Bound::Included(i) => *i,
std::ops::Bound::Excluded(i) => i.checked_add(1).unwrap(),

View file

@ -266,12 +266,12 @@ impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
let key = LintExpectationId::Unstable { attr_id, lint_index: None };
if !self.unstable_to_stable_ids.contains_key(&key) {
self.unstable_to_stable_ids.insert(
key,
LintExpectationId::Stable { hir_id, attr_index, lint_index: None, attr_id: None },
);
}
self.unstable_to_stable_ids.entry(key).or_insert(LintExpectationId::Stable {
hir_id,
attr_index,
lint_index: None,
attr_id: None,
});
self.expectations.push((id.normalize(), expectation));
}

View file

@ -30,7 +30,7 @@ pub unsafe extern "C" fn LLVMRustStringWriteImpl(
ptr: *const c_char,
size: size_t,
) {
let slice = slice::from_raw_parts(ptr as *const u8, size as usize);
let slice = slice::from_raw_parts(ptr as *const u8, size);
sr.bytes.borrow_mut().extend_from_slice(slice);
}

View file

@ -119,7 +119,7 @@ impl DiagnosticDeriveBuilder {
impl<'a> DiagnosticDeriveVariantBuilder<'a> {
/// Generates calls to `code` and similar functions based on the attributes on the type or
/// variant.
pub fn preamble<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream {
pub fn preamble(&mut self, variant: &VariantInfo<'_>) -> TokenStream {
let ast = variant.ast();
let attrs = &ast.attrs;
let preamble = attrs.iter().map(|attr| {
@ -133,7 +133,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
/// Generates calls to `span_label` and similar functions based on the attributes on fields or
/// calls to `set_arg` when no attributes are present.
pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream {
pub fn body(&mut self, variant: &VariantInfo<'_>) -> TokenStream {
let mut body = quote! {};
// Generate `set_arg` calls first..
for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) {

View file

@ -100,7 +100,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
Diagnostic::spanned(
resource_span,
Level::Error,
format!("could not open Fluent resource: {}", e.to_string()),
format!("could not open Fluent resource: {e}"),
)
.emit();
return failed(&crate_name);

View file

@ -762,14 +762,14 @@ impl<'a> CrateLocator<'a> {
}
pub(crate) fn into_error(self, root: Option<CratePaths>) -> CrateError {
CrateError::LocatorCombined(CombinedLocatorError {
CrateError::LocatorCombined(Box::new(CombinedLocatorError {
crate_name: self.crate_name,
root,
triple: self.triple,
dll_prefix: self.target.dll_prefix.to_string(),
dll_suffix: self.target.dll_suffix.to_string(),
crate_rejections: self.crate_rejections,
})
}))
}
}
@ -958,7 +958,7 @@ pub(crate) enum CrateError {
StableCrateIdCollision(Symbol, Symbol),
DlOpen(String),
DlSym(String),
LocatorCombined(CombinedLocatorError),
LocatorCombined(Box<CombinedLocatorError>),
NonDylibPlugin(Symbol),
}

View file

@ -74,18 +74,17 @@ impl<'hir> Iterator for ParentHirIterator<'hir> {
if self.current_id == CRATE_HIR_ID {
return None;
}
loop {
// There are nodes that do not have entries, so we need to skip them.
let parent_id = self.map.parent_id(self.current_id);
if parent_id == self.current_id {
self.current_id = CRATE_HIR_ID;
return None;
}
// There are nodes that do not have entries, so we need to skip them.
let parent_id = self.map.parent_id(self.current_id);
self.current_id = parent_id;
return Some(parent_id);
if parent_id == self.current_id {
self.current_id = CRATE_HIR_ID;
return None;
}
self.current_id = parent_id;
return Some(parent_id);
}
}

View file

@ -80,6 +80,18 @@ impl CanonicalVarValues<'_> {
}
})
}
pub fn is_identity_modulo_regions(&self) -> bool {
self.var_values.iter().enumerate().all(|(bv, arg)| match arg.unpack() {
ty::GenericArgKind::Lifetime(_) => true,
ty::GenericArgKind::Type(ty) => {
matches!(*ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var.as_usize() == bv)
}
ty::GenericArgKind::Const(ct) => {
matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.as_usize() == bv)
}
})
}
}
/// When we canonicalize a value to form a query, we wind up replacing

View file

@ -109,26 +109,34 @@ const MAX_HASHED_BUFFER_LEN: usize = 2 * MAX_BYTES_TO_HASH;
// large.
impl hash::Hash for Allocation {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
let Self {
bytes,
provenance,
init_mask,
align,
mutability,
extra: (), // don't bother hashing ()
} = self;
// Partially hash the `bytes` buffer when it is large. To limit collisions with common
// prefixes and suffixes, we hash the length and some slices of the buffer.
let byte_count = self.bytes.len();
let byte_count = bytes.len();
if byte_count > MAX_HASHED_BUFFER_LEN {
// Hash the buffer's length.
byte_count.hash(state);
// And its head and tail.
self.bytes[..MAX_BYTES_TO_HASH].hash(state);
self.bytes[byte_count - MAX_BYTES_TO_HASH..].hash(state);
bytes[..MAX_BYTES_TO_HASH].hash(state);
bytes[byte_count - MAX_BYTES_TO_HASH..].hash(state);
} else {
self.bytes.hash(state);
bytes.hash(state);
}
// Hash the other fields as usual.
self.provenance.hash(state);
self.init_mask.hash(state);
self.align.hash(state);
self.mutability.hash(state);
self.extra.hash(state);
provenance.hash(state);
init_mask.hash(state);
align.hash(state);
mutability.hash(state);
}
}

View file

@ -915,7 +915,7 @@ pub enum LocalInfo<'tcx> {
impl<'tcx> LocalDecl<'tcx> {
pub fn local_info(&self) -> &LocalInfo<'tcx> {
&**self.local_info.as_ref().assert_crate_local()
&self.local_info.as_ref().assert_crate_local()
}
/// Returns `true` only if local is a binding that can itself be

View file

@ -133,21 +133,21 @@ impl<'tcx> MirPatch<'tcx> {
let mut new_decl = LocalDecl::new(ty, span).internal();
**new_decl.local_info.as_mut().assert_crate_local() = local_info;
self.new_locals.push(new_decl);
Local::new(index as usize)
Local::new(index)
}
pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
let index = self.next_local;
self.next_local += 1;
self.new_locals.push(LocalDecl::new(ty, span));
Local::new(index as usize)
Local::new(index)
}
pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
let index = self.next_local;
self.next_local += 1;
self.new_locals.push(LocalDecl::new(ty, span).internal());
Local::new(index as usize)
Local::new(index)
}
pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {

View file

@ -98,7 +98,7 @@ impl<'tcx> PlaceTy<'tcx> {
ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64),
ty::Array(inner, size) if from_end => {
let size = size.eval_target_usize(tcx, param_env);
let len = size - (from as u64) - (to as u64);
let len = size - from - to;
tcx.mk_array(*inner, len)
}
_ => bug!("cannot subslice non-array type: `{:?}`", self),

View file

@ -178,17 +178,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
// When we yield `B` and call `traverse_successor`, we push `C` to the stack, but
// since we've already visited `E`, that child isn't added to the stack. The last
// two iterations yield `C` and finally `A` for a final traversal of [E, D, B, C, A]
loop {
let bb = if let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() {
if let Some(bb) = iter.next() {
bb
} else {
break;
}
} else {
break;
};
while let Some(&mut (_, ref mut iter)) = self.visit_stack.last_mut() && let Some(bb) = iter.next() {
if self.visited.insert(bb) {
if let Some(term) = &self.basic_blocks[bb].terminator {
self.visit_stack.push((bb, term.successors()));

View file

@ -923,7 +923,7 @@ impl ObjectSafetyViolation {
}
}
ObjectSafetyViolation::SupertraitNonLifetimeBinder(_) => {
format!("where clause cannot reference non-lifetime `for<...>` variables").into()
"where clause cannot reference non-lifetime `for<...>` variables".into()
}
ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => {
format!("associated function `{}` has no `self` parameter", name).into()

View file

@ -56,9 +56,19 @@ pub enum Certainty {
impl Certainty {
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
/// use this function to unify the certainty of these goals
pub fn unify_and(self, other: Certainty) -> Certainty {
/// Use this function to merge the certainty of multiple nested subgoals.
///
/// Given an impl like `impl<T: Foo + Bar> Baz for T {}`, we have 2 nested
/// subgoals whenever we use the impl as a candidate: `T: Foo` and `T: Bar`.
/// If evaluating `T: Foo` results in ambiguity and `T: Bar` results in
/// success, we merge these two responses. This results in ambiguity.
///
/// If we unify ambiguity with overflow, we return overflow. This doesn't matter
/// inside of the solver as we distinguish ambiguity from overflow. It does
/// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar`
/// in ambiguity without changing the inference state, we still want to tell the
/// user that `T: Baz` results in overflow.
pub fn unify_with(self, other: Certainty) -> Certainty {
match (self, other) {
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
(Certainty::Yes, Certainty::Maybe(_)) => other,
@ -105,7 +115,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
type Target = ExternalConstraintsData<'tcx>;
fn deref(&self) -> &Self::Target {
&*self.0
&self.0
}
}

View file

@ -337,7 +337,7 @@ impl ScalarInt {
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 16 }`
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i128(self) -> Result<i128, Size> {
self.try_to_int(Size::from_bits(128)).map(|v| i128::try_from(v).unwrap())
self.try_to_int(Size::from_bits(128))
}
}

View file

@ -924,7 +924,7 @@ impl<'tcx> TyCtxt<'tcx> {
crate_name,
// Don't print the whole stable crate id. That's just
// annoying in debug output.
stable_crate_id.to_u64() >> 8 * 6,
stable_crate_id.to_u64() >> (8 * 6),
self.def_path(def_id).to_string_no_crate_verbose()
)
}
@ -2379,7 +2379,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> {
let map = self.in_scope_traits_map(id.owner)?;
let candidates = map.get(&id.local_id)?;
Some(&*candidates)
Some(candidates)
}
pub fn named_bound_var(self, id: HirId) -> Option<resolve_bound_vars::ResolvedArg> {

View file

@ -1891,7 +1891,7 @@ impl<'tcx> Ty<'tcx> {
// The way we evaluate the `N` in `[T; N]` here only works since we use
// `simd_size_and_type` post-monomorphization. It will probably start to ICE
// if we use it in generic code. See the `simd-array-trait` ui test.
(f0_len.eval_target_usize(tcx, ParamEnv::empty()) as u64, *f0_elem_ty)
(f0_len.eval_target_usize(tcx, ParamEnv::empty()), *f0_elem_ty)
}
// Otherwise, the fields of this Adt are the SIMD components (and we assume they
// all have the same type).

View file

@ -558,8 +558,8 @@ impl<'a> StringReader<'a> {
}
if let Some(possible_offset) = possible_offset {
let lo = start + BytePos(possible_offset as u32);
let hi = lo + BytePos(found_terminators as u32);
let lo = start + BytePos(possible_offset);
let hi = lo + BytePos(found_terminators);
let span = self.mk_sp(lo, hi);
err.span_suggestion(
span,

View file

@ -336,8 +336,8 @@ const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[
("\"", "Quotation Mark", None),
];
pub(super) fn check_for_substitution<'a>(
reader: &StringReader<'a>,
pub(super) fn check_for_substitution(
reader: &StringReader<'_>,
pos: BytePos,
ch: char,
count: usize,

View file

@ -53,7 +53,7 @@ impl<'a> Parser<'a> {
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_ty() {
Ok(p) => {
if let TyKind::ImplTrait(_, bounds) = &(*p).kind {
if let TyKind::ImplTrait(_, bounds) = &p.kind {
let span = impl_span.to(self.token.span.shrink_to_lo());
let mut err = self.struct_span_err(
span,

View file

@ -136,7 +136,9 @@ where
}
fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) {
self.cache.lock().as_ref().map(|value| f(&(), &value.0, value.1));
if let Some(value) = self.cache.lock().as_ref() {
f(&(), &value.0, value.1)
}
}
}

View file

@ -1224,7 +1224,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
lifetime_ribs: Vec::new(),
lifetime_elision_candidates: None,
current_trait_ref: None,
diagnostic_metadata: Box::new(DiagnosticMetadata::default()),
diagnostic_metadata: Default::default(),
// errors at module scope should always be reported
in_func_body: false,
lifetime_uses: Default::default(),

View file

@ -1426,7 +1426,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
opt::opt_s(
"",
"edition",
&*EDITION_STRING,
&EDITION_STRING,
EDITION_NAME_LIST,
),
opt::multi_s(

View file

@ -84,12 +84,12 @@ impl SymbolGallery {
/// Construct a diagnostic for a language feature error due to the given `span`.
/// The `feature`'s `Symbol` is the one you used in `active.rs` and `rustc_span::symbols`.
pub fn feature_err<'a>(
sess: &'a ParseSess,
pub fn feature_err(
sess: &ParseSess,
feature: Symbol,
span: impl Into<MultiSpan>,
explain: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
feature_err_issue(sess, feature, span, GateIssue::Language, explain)
}
@ -98,20 +98,21 @@ pub fn feature_err<'a>(
/// This variant allows you to control whether it is a library or language feature.
/// Almost always, you want to use this for a language feature. If so, prefer `feature_err`.
#[track_caller]
pub fn feature_err_issue<'a>(
sess: &'a ParseSess,
pub fn feature_err_issue(
sess: &ParseSess,
feature: Symbol,
span: impl Into<MultiSpan>,
issue: GateIssue,
explain: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let span = span.into();
// Cancel an earlier warning for this same error, if it exists.
if let Some(span) = span.primary_span() {
sess.span_diagnostic
.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
.map(|err| err.cancel());
if let Some(err) = sess.span_diagnostic.steal_diagnostic(span, StashKey::EarlySyntaxWarning)
{
err.cancel()
}
}
let mut err = sess.create_err(FeatureGateError { span, explain: explain.into() });

View file

@ -1353,16 +1353,16 @@ impl Clone for SourceFile {
Self {
name: self.name.clone(),
src: self.src.clone(),
src_hash: self.src_hash.clone(),
src_hash: self.src_hash,
external_src: Lock::new(self.external_src.borrow().clone()),
start_pos: self.start_pos.clone(),
end_pos: self.end_pos.clone(),
start_pos: self.start_pos,
end_pos: self.end_pos,
lines: Lock::new(self.lines.borrow().clone()),
multibyte_chars: self.multibyte_chars.clone(),
non_narrow_chars: self.non_narrow_chars.clone(),
normalized_pos: self.normalized_pos.clone(),
name_hash: self.name_hash.clone(),
cnum: self.cnum.clone(),
name_hash: self.name_hash,
cnum: self.cnum,
}
}
}
@ -2051,13 +2051,13 @@ pub type FileLinesResult = Result<FileLines, SpanLinesError>;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum SpanLinesError {
DistinctSources(DistinctSources),
DistinctSources(Box<DistinctSources>),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum SpanSnippetError {
IllFormedSpan(Span),
DistinctSources(DistinctSources),
DistinctSources(Box<DistinctSources>),
MalformedForSourcemap(MalformedSourceMapPositions),
SourceNotAvailable { filename: FileName },
}

View file

@ -542,10 +542,10 @@ impl SourceMap {
let hi = self.lookup_char_pos(sp.hi());
trace!(?hi);
if lo.file.start_pos != hi.file.start_pos {
return Err(SpanLinesError::DistinctSources(DistinctSources {
return Err(SpanLinesError::DistinctSources(Box::new(DistinctSources {
begin: (lo.file.name.clone(), lo.file.start_pos),
end: (hi.file.name.clone(), hi.file.start_pos),
}));
})));
}
Ok((lo, hi))
}
@ -603,10 +603,10 @@ impl SourceMap {
let local_end = self.lookup_byte_offset(sp.hi());
if local_begin.sf.start_pos != local_end.sf.start_pos {
Err(SpanSnippetError::DistinctSources(DistinctSources {
Err(SpanSnippetError::DistinctSources(Box::new(DistinctSources {
begin: (local_begin.sf.name.clone(), local_begin.sf.start_pos),
end: (local_end.sf.name.clone(), local_end.sf.start_pos),
}))
})))
} else {
self.ensure_source_file_source_present(local_begin.sf.clone());

View file

@ -324,8 +324,6 @@ impl Abi {
impl fmt::Display for Abi {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
abi => write!(f, "\"{}\"", abi.name()),
}
write!(f, "\"{}\"", self.name())
}
}

View file

@ -1,11 +1,9 @@
//! Code shared by trait and projection goals for candidate assembly.
use super::search_graph::OverflowHandler;
#[cfg(doc)]
use super::trait_goals::structural_traits::*;
use super::{EvalCtxt, SolverMode};
use crate::solve::CanonicalResponseExt;
use crate::traits::coherence;
use itertools::Itertools;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
@ -16,6 +14,8 @@ use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use std::fmt::Debug;
pub(super) mod structural_traits;
/// A candidate is a possible way to prove a goal.
///
/// It consists of both the `source`, which describes how that goal would be proven,
@ -547,61 +547,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
/// If there are multiple ways to prove a trait or projection goal, we have
/// to somehow try to merge the candidates into one. If that fails, we return
/// ambiguity.
#[instrument(level = "debug", skip(self), ret)]
pub(super) fn merge_candidates(
&mut self,
mut candidates: Vec<Candidate<'tcx>>,
) -> QueryResult<'tcx> {
match candidates.len() {
0 => return Err(NoSolution),
1 => return Ok(candidates.pop().unwrap().result),
_ => {}
// First try merging all candidates. This is complete and fully sound.
let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
if let Some(result) = self.try_merge_responses(&responses) {
return Ok(result);
}
if candidates.len() > 1 {
let mut i = 0;
'outer: while i < candidates.len() {
for j in (0..candidates.len()).filter(|&j| i != j) {
if self.candidate_should_be_dropped_in_favor_of(&candidates[i], &candidates[j])
{
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
candidates.swap_remove(i);
continue 'outer;
// We then check whether we should prioritize `ParamEnv` candidates.
//
// Doing so is incomplete and would therefore be unsound during coherence.
match self.solver_mode() {
SolverMode::Coherence => (),
// Prioritize `ParamEnv` candidates only if they do not guide inference.
//
// This is still incomplete as we may add incorrect region bounds.
SolverMode::Normal => {
let param_env_responses = candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result)
.collect::<Vec<_>>();
if let Some(result) = self.try_merge_responses(&param_env_responses) {
if result.has_only_region_constraints() {
return Ok(result);
}
}
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
i += 1;
}
// If there are *STILL* multiple candidates that have *different* response
// results, give up and report ambiguity.
if candidates.len() > 1 && !candidates.iter().map(|cand| cand.result).all_equal() {
let certainty = if candidates.iter().all(|x| {
matches!(x.result.value.certainty, Certainty::Maybe(MaybeCause::Overflow))
}) {
Certainty::Maybe(MaybeCause::Overflow)
} else {
Certainty::AMBIGUOUS
};
return self.evaluate_added_goals_and_make_canonical_response(certainty);
}
}
Ok(candidates.pop().unwrap().result)
}
fn candidate_should_be_dropped_in_favor_of(
&self,
candidate: &Candidate<'tcx>,
other: &Candidate<'tcx>,
) -> bool {
// FIXME: implement this
match (candidate.source, other.source) {
(CandidateSource::Impl(_), _)
| (CandidateSource::ParamEnv(_), _)
| (CandidateSource::AliasBound, _)
| (CandidateSource::BuiltinImpl, _) => false,
}
self.flounder(&responses)
}
}

View file

@ -11,7 +11,7 @@ use crate::solve::EvalCtxt;
//
// For types with an "existential" binder, i.e. generator witnesses, we also
// instantiate the binder with placeholders eagerly.
pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>,
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@ -87,7 +87,7 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
}
}
fn replace_erased_lifetimes_with_bound_vars<'tcx>(
pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> ty::Binder<'tcx, Ty<'tcx>> {
@ -108,7 +108,7 @@ fn replace_erased_lifetimes_with_bound_vars<'tcx>(
ty::Binder::bind_with_vars(ty, bound_vars)
}
pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>,
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@ -158,7 +158,7 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
}
}
pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>,
ty: Ty<'tcx>,
) -> Result<Vec<Ty<'tcx>>, NoSolution> {
@ -224,7 +224,7 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
}
// Returns a binder of the tupled inputs types and output type from a builtin callable type.
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
tcx: TyCtxt<'tcx>,
self_ty: Ty<'tcx>,
goal_kind: ty::ClosureKind,
@ -337,7 +337,13 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
/// additional step of eagerly folding the associated types in the where
/// clauses of the impl. In this example, that means replacing
/// `<Self as Foo>::Bar` with `Ty` in the first impl.
pub(crate) fn predicates_for_object_candidate<'tcx>(
///
// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound`
// bounds in impls are trivially proven using the item bound candidates.
// This is unsound in general and once that is fixed, we don't need to
// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
// for more details.
pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
ecx: &EvalCtxt<'_, 'tcx>,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,

View file

@ -357,7 +357,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
// deal with `has_changed` in the next iteration.
new_goals.normalizes_to_hack_goal =
Some(this.resolve_vars_if_possible(goal));
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
has_changed = has_changed.map_err(|c| c.unify_with(certainty));
}
}
}
@ -378,7 +378,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
Certainty::Yes => {}
Certainty::Maybe(_) => {
new_goals.goals.push(goal);
has_changed = has_changed.map_err(|c| c.unify_and(certainty));
has_changed = has_changed.map_err(|c| c.unify_with(certainty));
}
}
}

View file

@ -50,7 +50,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
certainty: Certainty,
) -> QueryResult<'tcx> {
let goals_certainty = self.try_evaluate_added_goals()?;
let certainty = certainty.unify_and(goals_certainty);
let certainty = certainty.unify_with(goals_certainty);
let external_constraints = self.compute_external_query_constraints()?;

View file

@ -46,6 +46,8 @@ enum SolverMode {
trait CanonicalResponseExt {
fn has_no_inference_or_external_constraints(&self) -> bool;
fn has_only_region_constraints(&self) -> bool;
}
impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
@ -54,6 +56,11 @@ impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> {
&& self.value.var_values.is_identity()
&& self.value.external_constraints.opaque_types.is_empty()
}
fn has_only_region_constraints(&self) -> bool {
self.value.var_values.is_identity_modulo_regions()
&& self.value.external_constraints.opaque_types.is_empty()
}
}
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
@ -221,12 +228,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
(Some(alias_lhs), Some(alias_rhs)) => {
debug!("both sides are aliases");
let candidates = vec![
// LHS normalizes-to RHS
evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No),
// RHS normalizes-to RHS
evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes),
// Relate via substs
let mut candidates = Vec::new();
// LHS normalizes-to RHS
candidates.extend(
evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No).ok(),
);
// RHS normalizes-to RHS
candidates.extend(
evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes).ok(),
);
// Relate via substs
candidates.extend(
self.probe(|ecx| {
let span = tracing::span!(
tracing::Level::DEBUG,
@ -247,11 +259,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
}
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}),
];
})
.ok(),
);
debug!(?candidates);
self.try_merge_responses(candidates.into_iter())
if let Some(merged) = self.try_merge_responses(&candidates) {
Ok(merged)
} else {
self.flounder(&candidates)
}
}
}
}
@ -289,43 +306,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
debug!("added_goals={:?}", &self.nested_goals.goals[current_len..]);
}
#[instrument(level = "debug", skip(self, responses))]
/// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
///
/// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
#[instrument(level = "debug", skip(self), ret)]
fn try_merge_responses(
&mut self,
responses: impl Iterator<Item = QueryResult<'tcx>>,
) -> QueryResult<'tcx> {
let candidates = responses.into_iter().flatten().collect::<Box<[_]>>();
if candidates.is_empty() {
return Err(NoSolution);
responses: &[CanonicalResponse<'tcx>],
) -> Option<CanonicalResponse<'tcx>> {
if responses.is_empty() {
return None;
}
// FIXME(-Ztrait-solver=next): We should instead try to find a `Certainty::Yes` response with
// a subset of the constraints that all the other responses have.
let one = candidates[0];
if candidates[1..].iter().all(|resp| resp == &one) {
return Ok(one);
let one = responses[0];
if responses[1..].iter().all(|&resp| resp == one) {
return Some(one);
}
if let Some(response) = candidates.iter().find(|response| {
response.value.certainty == Certainty::Yes
&& response.has_no_inference_or_external_constraints()
}) {
return Ok(*response);
}
responses
.iter()
.find(|response| {
response.value.certainty == Certainty::Yes
&& response.has_no_inference_or_external_constraints()
})
.copied()
}
let certainty = candidates.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_and(response.value.certainty)
/// If we fail to merge responses we flounder and return overflow or ambiguity.
#[instrument(level = "debug", skip(self), ret)]
fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
if responses.is_empty() {
return Err(NoSolution);
}
let certainty = responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
certainty.unify_with(response.value.certainty)
});
// FIXME(-Ztrait-solver=next): We should take the intersection of the constraints on all the
// responses and use that for the constraints of this ambiguous response.
debug!(">1 response, bailing with {certainty:?}");
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
if let Ok(response) = &response {
assert!(response.has_no_inference_or_external_constraints());
}
response
let response = self.evaluate_added_goals_and_make_canonical_response(certainty);
if let Ok(response) = response {
assert!(response.has_no_inference_or_external_constraints());
Ok(response)
} else {
bug!("failed to make floundered response: {responses:?}");
}
}
}

View file

@ -1,7 +1,6 @@
use crate::traits::specialization_graph;
use super::assembly;
use super::trait_goals::structural_traits;
use super::assembly::{self, structural_traits};
use super::EvalCtxt;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;

View file

@ -1,6 +1,7 @@
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
use super::{assembly, EvalCtxt, SolverMode};
use super::assembly::{self, structural_traits};
use super::{EvalCtxt, SolverMode};
use rustc_hir::def_id::DefId;
use rustc_hir::LangItem;
use rustc_infer::traits::query::NoSolution;
@ -11,8 +12,6 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
use rustc_span::DUMMY_SP;
pub mod structural_traits;
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
fn self_ty(self) -> Ty<'tcx> {
self.self_ty()

View file

@ -149,7 +149,7 @@ mod rustc {
.iter()
.enumerate()
.find(|(_, field_def)| name == field_def.name)
.expect(&format!("There were no fields named `{name}`."));
.unwrap_or_else(|| panic!("There were no fields named `{name}`."));
fields[field_idx].unwrap_leaf() == ScalarInt::TRUE
};

View file

@ -1241,13 +1241,9 @@ impl<T> MaybeUninit<T> {
/// ```
#[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")]
pub fn slice_as_bytes(this: &[MaybeUninit<T>]) -> &[MaybeUninit<u8>] {
let bytes = mem::size_of_val(this);
// SAFETY: MaybeUninit<u8> is always valid, even for padding bytes
unsafe {
slice::from_raw_parts(
this.as_ptr() as *const MaybeUninit<u8>,
this.len() * mem::size_of::<T>(),
)
}
unsafe { slice::from_raw_parts(this.as_ptr() as *const MaybeUninit<u8>, bytes) }
}
/// Returns the contents of this mutable slice of `MaybeUninit` as a mutable slice of
@ -1274,13 +1270,9 @@ impl<T> MaybeUninit<T> {
/// ```
#[unstable(feature = "maybe_uninit_as_bytes", issue = "93092")]
pub fn slice_as_bytes_mut(this: &mut [MaybeUninit<T>]) -> &mut [MaybeUninit<u8>] {
let bytes = mem::size_of_val(this);
// SAFETY: MaybeUninit<u8> is always valid, even for padding bytes
unsafe {
slice::from_raw_parts_mut(
this.as_mut_ptr() as *mut MaybeUninit<u8>,
this.len() * mem::size_of::<T>(),
)
}
unsafe { slice::from_raw_parts_mut(this.as_mut_ptr() as *mut MaybeUninit<u8>, bytes) }
}
}

View file

@ -271,9 +271,17 @@ impl Step for Rustc {
false,
);
let libdir = builder.sysroot_libdir(compiler, target);
let hostdir = builder.sysroot_libdir(compiler, compiler.host);
add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
// HACK: This avoids putting the newly built artifacts in the sysroot if we're using
// `download-rustc`, to avoid "multiple candidates for `rmeta`" errors. Technically, that's
// not quite right: people can set `download-rustc = true` to download even if there are
// changes to the compiler, and in that case ideally we would put the *new* artifacts in the
// sysroot, in case there are API changes that should be used by tools. In practice,
// though, that should be very uncommon, and people can still disable download-rustc.
if !builder.download_rustc() {
let libdir = builder.sysroot_libdir(compiler, target);
let hostdir = builder.sysroot_libdir(compiler, compiler.host);
add_to_sysroot(&builder, &libdir, &hostdir, &librustc_stamp(builder, compiler, target));
}
}
}

View file

@ -6,6 +6,7 @@ use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process::Command;
use build_helper::ci::CiEnv;
use tracing::*;
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
@ -276,8 +277,12 @@ impl TestProps {
/// `//[foo]`), then the property is ignored unless `cfg` is
/// `Some("foo")`.
fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
// Mode-dependent defaults.
self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc");
// In CI, we've sometimes encountered non-determinism related to truncating very long paths.
// Set a consistent (short) prefix to avoid issues, but only in CI to avoid regressing the
// contributor experience.
if CiEnv::is_ci() {
self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc");
}
let mut has_edition = false;
if !testfile.is_dir() {

View file

@ -0,0 +1,35 @@
// compile-flags: -O -C no-prepopulate-passes
// only-x86_64 (it's using arch-specific types)
// min-llvm-version: 15.0 # this test assumes `ptr`s
#![crate_type = "lib"]
use std::arch::x86_64::{__m128, __m128i, __m256i};
use std::mem::transmute;
// CHECK-LABEL: @check_sse_float_to_int(
#[no_mangle]
pub unsafe fn check_sse_float_to_int(x: __m128) -> __m128i {
// CHECK-NOT: alloca
// CHECK: %1 = load <4 x float>, ptr %x, align 16
// CHECK: store <4 x float> %1, ptr %0, align 16
transmute(x)
}
// CHECK-LABEL: @check_sse_pair_to_avx(
#[no_mangle]
pub unsafe fn check_sse_pair_to_avx(x: (__m128i, __m128i)) -> __m256i {
// CHECK-NOT: alloca
// CHECK: %1 = load <4 x i64>, ptr %x, align 16
// CHECK: store <4 x i64> %1, ptr %0, align 32
transmute(x)
}
// CHECK-LABEL: @check_sse_pair_from_avx(
#[no_mangle]
pub unsafe fn check_sse_pair_from_avx(x: __m256i) -> (__m128i, __m128i) {
// CHECK-NOT: alloca
// CHECK: %1 = load <4 x i64>, ptr %x, align 32
// CHECK: store <4 x i64> %1, ptr %0, align 16
transmute(x)
}

View file

@ -8,7 +8,7 @@
#![feature(inline_const)]
#![allow(unreachable_code)]
use std::mem::transmute;
use std::mem::{transmute, MaybeUninit};
// Some of the cases here are statically rejected by `mem::transmute`, so
// we need to generate custom MIR for those cases to get to codegen.
@ -54,6 +54,32 @@ pub unsafe fn check_smaller_size(x: u32) -> u16 {
}
}
// CHECK-LABEL: @check_smaller_array(
#[no_mangle]
#[custom_mir(dialect = "runtime", phase = "initial")]
pub unsafe fn check_smaller_array(x: [u32; 7]) -> [u32; 3] {
// CHECK: call void @llvm.trap
mir!{
{
RET = CastTransmute(x);
Return()
}
}
}
// CHECK-LABEL: @check_bigger_array(
#[no_mangle]
#[custom_mir(dialect = "runtime", phase = "initial")]
pub unsafe fn check_bigger_array(x: [u32; 3]) -> [u32; 7] {
// CHECK: call void @llvm.trap
mir!{
{
RET = CastTransmute(x);
Return()
}
}
}
// CHECK-LABEL: @check_to_uninhabited(
#[no_mangle]
#[custom_mir(dialect = "runtime", phase = "initial")]
@ -71,7 +97,7 @@ pub unsafe fn check_to_uninhabited(x: u16) -> BigNever {
#[no_mangle]
#[custom_mir(dialect = "runtime", phase = "initial")]
pub unsafe fn check_from_uninhabited(x: BigNever) -> u16 {
// CHECK: call void @llvm.trap
// CHECK: ret i16 poison
mir!{
{
RET = CastTransmute(x);
@ -301,3 +327,105 @@ pub unsafe fn check_pair_to_array(x: (i64, u64)) -> [u8; 16] {
// CHECK: store i64 %x.1, ptr %{{.+}}, align 1
transmute(x)
}
// CHECK-LABEL: @check_heterogeneous_integer_pair(
#[no_mangle]
pub unsafe fn check_heterogeneous_integer_pair(x: (i32, bool)) -> (bool, u32) {
// CHECK: store i32 %x.0
// CHECK: %[[WIDER:.+]] = zext i1 %x.1 to i8
// CHECK: store i8 %[[WIDER]]
// CHECK: %[[BYTE:.+]] = load i8
// CHECK: trunc i8 %[[BYTE:.+]] to i1
// CHECK: load i32
transmute(x)
}
// CHECK-LABEL: @check_heterogeneous_float_pair(
#[no_mangle]
pub unsafe fn check_heterogeneous_float_pair(x: (f64, f32)) -> (f32, f64) {
// CHECK: store double %x.0
// CHECK: store float %x.1
// CHECK: %[[A:.+]] = load float
// CHECK: %[[B:.+]] = load double
// CHECK: %[[P:.+]] = insertvalue { float, double } poison, float %[[A]], 0
// CHECK: insertvalue { float, double } %[[P]], double %[[B]], 1
transmute(x)
}
// CHECK-LABEL: @check_issue_110005(
#[no_mangle]
pub unsafe fn check_issue_110005(x: (usize, bool)) -> Option<Box<[u8]>> {
// CHECK: store i64 %x.0
// CHECK: %[[WIDER:.+]] = zext i1 %x.1 to i8
// CHECK: store i8 %[[WIDER]]
// CHECK: load ptr
// CHECK: load i64
transmute(x)
}
// CHECK-LABEL: @check_pair_to_dst_ref(
#[no_mangle]
pub unsafe fn check_pair_to_dst_ref<'a>(x: (usize, usize)) -> &'a [u8] {
// CHECK: %0 = inttoptr i64 %x.0 to ptr
// CHECK: %1 = insertvalue { ptr, i64 } poison, ptr %0, 0
// CHECK: %2 = insertvalue { ptr, i64 } %1, i64 %x.1, 1
// CHECK: ret { ptr, i64 } %2
transmute(x)
}
// CHECK-LABEL: @check_issue_109992(
#[no_mangle]
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub unsafe fn check_issue_109992(x: ()) -> [(); 1] {
// This uses custom MIR to avoid MIR optimizations having removed ZST ops.
// CHECK: start
// CHECK-NEXT: ret void
mir!{
{
RET = CastTransmute(x);
Return()
}
}
}
// CHECK-LABEL: @check_maybe_uninit_pair(i16 %x.0, i64 %x.1)
#[no_mangle]
pub unsafe fn check_maybe_uninit_pair(
x: (MaybeUninit<u16>, MaybeUninit<u64>),
) -> (MaybeUninit<i64>, MaybeUninit<i16>) {
// Thanks to `MaybeUninit` this is actually defined behaviour,
// unlike the examples above with pairs of primitives.
// CHECK: store i16 %x.0
// CHECK: store i64 %x.1
// CHECK: load i64
// CHECK-NOT: noundef
// CHECK: load i16
// CHECK-NOT: noundef
// CHECK: ret { i64, i16 }
transmute(x)
}
#[repr(align(8))]
pub struct HighAlignScalar(u8);
// CHECK-LABEL: @check_to_overalign(
#[no_mangle]
pub unsafe fn check_to_overalign(x: u64) -> HighAlignScalar {
// CHECK: %0 = alloca %HighAlignScalar, align 8
// CHECK: store i64 %x, ptr %0, align 8
// CHECK: %1 = load i64, ptr %0, align 8
// CHECK: ret i64 %1
transmute(x)
}
// CHECK-LABEL: @check_from_overalign(
#[no_mangle]
pub unsafe fn check_from_overalign(x: HighAlignScalar) -> u64 {
// CHECK: %x = alloca %HighAlignScalar, align 8
// CHECK: %[[VAL:.+]] = load i64, ptr %x, align 8
// CHECK: ret i64 %[[VAL]]
transmute(x)
}

View file

@ -0,0 +1,18 @@
error[E0282]: type annotations needed
--> $DIR/issue-95230.rs:9:13
|
LL | for<'a> &'a mut Self:;
| ^^^^^^^^^^^^ cannot infer type for mutable reference `&'a mut Bar`
|
note: required by a bound in `Bar`
--> $DIR/issue-95230.rs:9:13
|
LL | pub struct Bar
| --- required by a bound in this struct
LL | where
LL | for<'a> &'a mut Self:;
| ^^^^^^^^^^^^ required by this bound in `Bar`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.

View file

@ -1,4 +1,8 @@
// check-pass
// revisions: old new
//[new] compile-flags: -Ztrait-solver=next
//[old] check-pass
//[new] known-bug: #109764
pub struct Bar
where

View file

@ -1,7 +1,7 @@
// compile-flags: -Ztrait-solver=next
// check that when computing `alias-eq(<() as Foo<u16, T>>::Assoc, <() as Foo<?0, T>>::Assoc)`
// we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by
// we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by
// relating substs as either could be a valid solution.
trait Foo<T, STOP> {

View file

@ -0,0 +1,22 @@
// compile-flags: -Ztrait-solver=next
// check-pass
trait Foo {}
impl<T> Foo for T {}
trait Bar {}
struct Wrapper<'a, T>(&'a T);
impl<'a, T> Bar for Wrapper<'a, T> where &'a T: Foo {}
// We need to satisfy `&'a T: Foo` when checking that this impl is WF
// that can either be satisfied via the param-env, or via an impl.
//
// When satisfied via the param-env, since each lifetime is canonicalized
// separately, we end up getting extra region constraints.
//
// However, when satisfied via the impl, there are no region constraints,
// and we can short-circuit a response with no external constraints.
fn main() {}

View file

@ -0,0 +1,10 @@
// compile-flags: -Ztrait-solver=next
// check-pass
trait Foo<'a> {}
trait Bar<'a> {}
impl<'a, T: Bar<'a>> Foo<'a> for T {}
impl<T> Bar<'static> for T {}
fn main() {}

View file

@ -1,9 +1,16 @@
error[E0282]: type annotations needed
error[E0283]: type annotations needed: cannot satisfy `<T as Foo1>::Assoc1: Bar`
--> $DIR/recursive-self-normalization-2.rs:15:5
|
LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: cannot satisfy `<T as Foo1>::Assoc1: Bar`
note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization-2.rs:12:17
|
LL | fn needs_bar<S: Bar>() {}
| ^^^ required by this bound in `needs_bar`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
For more information about this error, try `rustc --explain E0283`.

View file

@ -1,9 +1,16 @@
error[E0282]: type annotations needed
error[E0283]: type annotations needed: cannot satisfy `<T as Foo>::Assoc: Bar`
--> $DIR/recursive-self-normalization.rs:11:5
|
LL | needs_bar::<T::Assoc>();
| ^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `S` declared on the function `needs_bar`
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: cannot satisfy `<T as Foo>::Assoc: Bar`
note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization.rs:8:17
|
LL | fn needs_bar<S: Bar>() {}
| ^^^ required by this bound in `needs_bar`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
For more information about this error, try `rustc --explain E0283`.