Skip to content

Use structurally_normalize instead of manual normalizes-to goals in alias relate errors #135816

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/type_check/canonical.rs
Original file line number Diff line number Diff line change
@@ -185,7 +185,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
CustomTypeOp::new(
|ocx| {
let structurally_normalize = |ty| {
ocx.structurally_normalize(
ocx.structurally_normalize_ty(
&ObligationCause::misc(
location.to_locations().span(body),
body.source.def_id().expect_local(),
@@ -230,7 +230,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::Boring,
CustomTypeOp::new(
|ocx| {
ocx.structurally_normalize(
ocx.structurally_normalize_ty(
&ObligationCause::misc(
location.to_locations().span(body),
body.source.def_id().expect_local(),
8 changes: 4 additions & 4 deletions compiler/rustc_hir_analysis/src/autoderef.rs
Original file line number Diff line number Diff line change
@@ -86,7 +86,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
if self.infcx.next_trait_solver()
&& let ty::Alias(..) = ty.kind()
{
let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
let (normalized_ty, obligations) = self.structurally_normalize_ty(ty)?;
self.state.obligations.extend(obligations);
(AutoderefKind::Builtin, normalized_ty)
} else {
@@ -166,20 +166,20 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
}

let (normalized_ty, obligations) =
self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
self.structurally_normalize_ty(Ty::new_projection(tcx, trait_target_def_id, [ty]))?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);

Some(self.infcx.resolve_vars_if_possible(normalized_ty))
}

#[instrument(level = "debug", skip(self), ret)]
pub fn structurally_normalize(
pub fn structurally_normalize_ty(
&self,
ty: Ty<'tcx>,
) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> {
let ocx = ObligationCtxt::new(self.infcx);
let Ok(normalized_ty) = ocx.structurally_normalize(
let Ok(normalized_ty) = ocx.structurally_normalize_ty(
&traits::ObligationCause::misc(self.span, self.body_id),
self.param_env,
ty,
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/coherence/orphan.rs
Original file line number Diff line number Diff line change
@@ -320,7 +320,7 @@ fn orphan_check<'tcx>(
}

let ty = if infcx.next_trait_solver() {
ocx.structurally_normalize(
ocx.structurally_normalize_ty(
&cause,
ty::ParamEnv::empty(),
infcx.resolve_vars_if_possible(ty),
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
@@ -1124,7 +1124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.next_trait_solver()
&& let ty::Alias(..) = ty.kind()
{
ocx.structurally_normalize(&cause, self.param_env, ty)
ocx.structurally_normalize_ty(&cause, self.param_env, ty)
} else {
Ok(ty)
}
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
@@ -1433,7 +1433,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// in a reentrant borrow, causing an ICE.
let result = self
.at(&self.misc(sp), self.param_env)
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut());
.structurally_normalize_ty(ty, &mut **self.fulfillment_cx.borrow_mut());
match result {
Ok(normalized_ty) => normalized_ty,
Err(errors) => {
42 changes: 19 additions & 23 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
@@ -277,23 +277,7 @@ where
param_env: I::ParamEnv,
ty: I::Ty,
) -> Result<I::Ty, NoSolution> {
if let ty::Alias(..) = ty.kind() {
let normalized_ty = self.next_ty_infer();
let alias_relate_goal = Goal::new(
self.cx(),
param_env,
ty::PredicateKind::AliasRelate(
ty.into(),
normalized_ty.into(),
ty::AliasRelationDirection::Equate,
),
);
self.add_goal(GoalSource::Misc, alias_relate_goal);
self.try_evaluate_added_goals()?;
Ok(self.resolve_vars_if_possible(normalized_ty))
} else {
Ok(ty)
}
self.structurally_normalize_term(param_env, ty.into()).map(|term| term.expect_ty())
}

/// Normalize a const for when it is structurally matched on, or more likely
@@ -308,22 +292,34 @@ where
param_env: I::ParamEnv,
ct: I::Const,
) -> Result<I::Const, NoSolution> {
if let ty::ConstKind::Unevaluated(..) = ct.kind() {
let normalized_ct = self.next_const_infer();
self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const())
}

/// Normalize a term for when it is structurally matched on.
///
/// This function is necessary in nearly all cases before matching on a ty/const.
/// Not doing so is likely to be incomplete and therefore unsound during coherence.
fn structurally_normalize_term(
&mut self,
param_env: I::ParamEnv,
term: I::Term,
) -> Result<I::Term, NoSolution> {
if let Some(_) = term.to_alias_term() {
let normalized_term = self.next_term_infer_of_kind(term);
let alias_relate_goal = Goal::new(
self.cx(),
param_env,
ty::PredicateKind::AliasRelate(
ct.into(),
normalized_ct.into(),
term,
normalized_term,
ty::AliasRelationDirection::Equate,
),
);
self.add_goal(GoalSource::Misc, alias_relate_goal);
self.try_evaluate_added_goals()?;
Ok(self.resolve_vars_if_possible(normalized_ct))
Ok(self.resolve_vars_if_possible(normalized_term))
} else {
Ok(ct)
Ok(term)
}
}

Original file line number Diff line number Diff line change
@@ -1338,20 +1338,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
let derive_better_type_error =
|alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| {
let ocx = ObligationCtxt::new(self);
let normalized_term = match expected_term.unpack() {
ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(),
ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
};
ocx.register_obligation(Obligation::new(
self.tcx,
ObligationCause::dummy(),

let Ok(normalized_term) = ocx.structurally_normalize_term(
&ObligationCause::dummy(),
obligation.param_env,
ty::PredicateKind::NormalizesTo(ty::NormalizesTo {
alias: alias_term,
term: normalized_term,
}),
));
let _ = ocx.select_where_possible();
alias_term.to_term(self.tcx),
) else {
return None;
};

if let Err(terr) = ocx.eq(
&ObligationCause::dummy(),
obligation.param_env,
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
@@ -709,7 +709,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
if matches!(ty.kind(), ty::Alias(..)) {
let ocx = ObligationCtxt::new(infcx);
ty = ocx
.structurally_normalize(&ObligationCause::dummy(), param_env, ty)
.structurally_normalize_ty(&ObligationCause::dummy(), param_env, ty)
.map_err(|_| ())?;
if !ocx.select_where_possible().is_empty() {
return Err(());
15 changes: 13 additions & 2 deletions compiler/rustc_trait_selection/src/traits/engine.rs
Original file line number Diff line number Diff line change
@@ -319,15 +319,15 @@ where
self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut())
}

pub fn structurally_normalize(
pub fn structurally_normalize_ty(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: Ty<'tcx>,
) -> Result<Ty<'tcx>, Vec<E>> {
self.infcx
.at(cause, param_env)
.structurally_normalize(value, &mut **self.engine.borrow_mut())
.structurally_normalize_ty(value, &mut **self.engine.borrow_mut())
}

pub fn structurally_normalize_const(
@@ -340,4 +340,15 @@ where
.at(cause, param_env)
.structurally_normalize_const(value, &mut **self.engine.borrow_mut())
}

pub fn structurally_normalize_term(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: ty::Term<'tcx>,
) -> Result<ty::Term<'tcx>, Vec<E>> {
self.infcx
.at(cause, param_env)
.structurally_normalize_term(value, &mut **self.engine.borrow_mut())
}
}
73 changes: 25 additions & 48 deletions compiler/rustc_trait_selection/src/traits/structural_normalize.rs
Original file line number Diff line number Diff line change
@@ -7,59 +7,42 @@ use crate::traits::{NormalizeExt, Obligation};

#[extension(pub trait StructurallyNormalizeExt<'tcx>)]
impl<'tcx> At<'_, 'tcx> {
fn structurally_normalize<E: 'tcx>(
fn structurally_normalize_ty<E: 'tcx>(
&self,
ty: Ty<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<Ty<'tcx>, Vec<E>> {
assert!(!ty.is_ty_var(), "should have resolved vars before calling");

if self.infcx.next_trait_solver() {
let ty::Alias(..) = *ty.kind() else {
return Ok(ty);
};

let new_infer_ty = self.infcx.next_ty_var(self.cause.span);

// We simply emit an `alias-eq` goal here, since that will take care of
// normalizing the LHS of the projection until it is a rigid projection
// (or a not-yet-defined opaque in scope).
let obligation = Obligation::new(
self.infcx.tcx,
self.cause.clone(),
self.param_env,
ty::PredicateKind::AliasRelate(
ty.into(),
new_infer_ty.into(),
ty::AliasRelationDirection::Equate,
),
);

fulfill_cx.register_predicate_obligation(self.infcx, obligation);
let errors = fulfill_cx.select_where_possible(self.infcx);
if !errors.is_empty() {
return Err(errors);
}

Ok(self.infcx.resolve_vars_if_possible(new_infer_ty))
} else {
Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx))
}
self.structurally_normalize_term(ty.into(), fulfill_cx).map(|term| term.expect_type())
}

fn structurally_normalize_const<E: 'tcx>(
&self,
ct: ty::Const<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<ty::Const<'tcx>, Vec<E>> {
assert!(!ct.is_ct_infer(), "should have resolved vars before calling");
if self.infcx.tcx.features().generic_const_exprs() {
return Ok(super::evaluate_const(&self.infcx, ct, self.param_env));
}

self.structurally_normalize_term(ct.into(), fulfill_cx).map(|term| term.expect_const())
}

fn structurally_normalize_term<E: 'tcx>(
&self,
term: ty::Term<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<ty::Term<'tcx>, Vec<E>> {
assert!(!term.is_infer(), "should have resolved vars before calling");

if self.infcx.next_trait_solver() {
let ty::ConstKind::Unevaluated(..) = ct.kind() else {
return Ok(ct);
};
if let None = term.to_alias_term() {
return Ok(term);
}

let new_infer_ct = self.infcx.next_const_var(self.cause.span);
let new_infer = match term.unpack() {
ty::TermKind::Ty(_) => self.infcx.next_ty_var(self.cause.span).into(),
ty::TermKind::Const(_) => self.infcx.next_const_var(self.cause.span).into(),
};

// We simply emit an `alias-eq` goal here, since that will take care of
// normalizing the LHS of the projection until it is a rigid projection
@@ -68,11 +51,7 @@ impl<'tcx> At<'_, 'tcx> {
self.infcx.tcx,
self.cause.clone(),
self.param_env,
ty::PredicateKind::AliasRelate(
ct.into(),
new_infer_ct.into(),
ty::AliasRelationDirection::Equate,
),
ty::PredicateKind::AliasRelate(term, new_infer, ty::AliasRelationDirection::Equate),
);

fulfill_cx.register_predicate_obligation(self.infcx, obligation);
@@ -81,11 +60,9 @@ impl<'tcx> At<'_, 'tcx> {
return Err(errors);
}

Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
} else if self.infcx.tcx.features().generic_const_exprs() {
Ok(super::evaluate_const(&self.infcx, ct, self.param_env))
Ok(self.infcx.resolve_vars_if_possible(new_infer))
} else {
Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
Ok(self.normalize(term).into_value_registering_obligations(self.infcx, fulfill_cx))
}
}
}
2 changes: 1 addition & 1 deletion src/tools/tidy/src/ui_tests.rs
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ use ignore::Walk;
const ENTRY_LIMIT: u32 = 901;
// FIXME: The following limits should be reduced eventually.

const ISSUES_ENTRY_LIMIT: u32 = 1663;
const ISSUES_ENTRY_LIMIT: u32 = 1664;

const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
error[E0271]: expected `{async [email protected]:5:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:5:23: 5:25}`
--> $DIR/is-not-fn.rs:5:14
error[E0271]: expected `{async [email protected]:8:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
--> $DIR/is-not-fn.rs:8:14
|
LL | needs_fn(async || {});
| -------- ^^^^^^^^^^^ expected `()`, found `async` closure body
| |
| required by a bound introduced by this call
|
= note: expected unit type `()`
found `async` closure body `{async closure body@$DIR/is-not-fn.rs:5:23: 5:25}`
found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
note: required by a bound in `needs_fn`
--> $DIR/is-not-fn.rs:4:25
--> $DIR/is-not-fn.rs:7:25
|
LL | fn needs_fn(x: impl FnOnce()) {}
| ^^^^^^^^ required by this bound in `needs_fn`
19 changes: 19 additions & 0 deletions tests/ui/async-await/async-closures/is-not-fn.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
error[E0271]: expected `{async [email protected]:8:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
--> $DIR/is-not-fn.rs:8:14
|
LL | needs_fn(async || {});
| -------- ^^^^^^^^^^^ expected `()`, found `async` closure body
| |
| required by a bound introduced by this call
|
= note: expected unit type `()`
found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
note: required by a bound in `needs_fn`
--> $DIR/is-not-fn.rs:7:25
|
LL | fn needs_fn(x: impl FnOnce()) {}
| ^^^^^^^^ required by this bound in `needs_fn`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0271`.
5 changes: 4 additions & 1 deletion tests/ui/async-await/async-closures/is-not-fn.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//@ edition:2021
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver

fn main() {
fn needs_fn(x: impl FnOnce()) {}
needs_fn(async || {});
//~^ ERROR expected `{async [email protected]:5:14}` to be a closure that returns `()`
//~^ ERROR expected `{async [email protected]:8:14}` to be a closure that returns `()`
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0407]: method `line_stream` is not a member of trait `X`
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:5
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5
|
LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X`

error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:22:21
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:21
|
LL | type LineStream<'a, Repr>
| -- ----
@@ -18,7 +18,7 @@ LL | type LineStream<'c, 'd> = impl Stream;
| found 0 type parameters

error[E0277]: `()` is not a future
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:43
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:43
|
LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error[E0407]: method `line_stream` is not a member of trait `X`
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:5
|
LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `X`

error[E0049]: type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:25:21
|
LL | type LineStream<'a, Repr>
| -- ----
| |
| expected 1 type parameter
...
LL | type LineStream<'c, 'd> = impl Stream;
| ^^ ^^
| |
| found 0 type parameters

error[E0271]: type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
--> $DIR/ice-unexpected-param-type-whensubstituting-in-region-112823.rs:28:43
|
LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0049, E0271, E0407.
For more information about an error, try `rustc --explain E0049`.
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
// test for ICE #112823
// Unexpected parameter Type(Repr) when substituting in region

@@ -23,8 +26,9 @@ impl X for Y {
//~^ ERROR type `LineStream` has 0 type parameters but its trait declaration has 1 type parameter
type LineStreamFut<'a, Repr> = impl Future<Output = Self::LineStream<'a, Repr>>;
fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {}
//~^ ERROR `()` is not a future
//~^^ method `line_stream` is not a member of trait `X`
//[current]~^ ERROR `()` is not a future
//[next]~^^ ERROR type mismatch resolving `<Y as X>::LineStreamFut<'a, Repr> == ()`
//~^^^ method `line_stream` is not a member of trait `X`
}

pub fn main() {}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
--> $DIR/issue-33941.rs:6:36
--> $DIR/issue-33941.rs:9:36
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^ expected `&_`, found `(&_, &_)`
|
= note: expected reference `&_`
found tuple `(&_, &_)`
note: the method call chain might not have had the expected associated types
--> $DIR/issue-33941.rs:6:29
--> $DIR/issue-33941.rs:9:29
|
LL | for _ in HashMap::new().iter().cloned() {}
| -------------- ^^^^^^ `Iterator::Item` is `(&_, &_)` here
@@ -17,7 +17,7 @@ note: required by a bound in `cloned`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
--> $DIR/issue-33941.rs:6:14
--> $DIR/issue-33941.rs:9:14
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)`
25 changes: 25 additions & 0 deletions tests/ui/issues/issue-33941.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
--> $DIR/issue-33941.rs:9:36
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^ expected `&_`, found `(&_, &_)`
|
= note: expected reference `&_`
found tuple `(&_, &_)`
note: required by a bound in `cloned`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
--> $DIR/issue-33941.rs:9:14
|
LL | for _ in HashMap::new().iter().cloned() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&_`, found `(&_, &_)`
|
= note: expected reference `&_`
found tuple `(&_, &_)`
= note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `Iterator`
= note: required for `Cloned<std::collections::hash_map::Iter<'_, _, _>>` to implement `IntoIterator`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0271`.
6 changes: 5 additions & 1 deletion tests/ui/issues/issue-33941.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ compile-flags: -Zdeduplicate-diagnostics=yes

use std::collections::HashMap;

fn main() {
for _ in HashMap::new().iter().cloned() {} //~ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
for _ in HashMap::new().iter().cloned() {}
//~^ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
//~| ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@[current] run-rustfix
fn main() {
let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected a `FnMut(&<RangeInclusive<{integer}> as Iterator>::Item)` closure, found
let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:3:24
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:24
|
LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0);
| ^^^^ -------- found signature defined here
@@ -16,7 +16,7 @@ LL | let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
| +

error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:4:24
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24
|
LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
| ^^^^ ----------- found signature defined here

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0277]: expected a `FnMut(&<RangeInclusive<{integer}> as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}`
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:6:29
|
LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0);
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut(&<RangeInclusive<{integer}> as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}`
| |
| required by a bound introduced by this call
|
= help: the trait `for<'a> FnMut(&'a <RangeInclusive<{integer}> as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}`
= note: expected a closure with arguments `(i32,)`
found a closure with arguments `(&<RangeInclusive<{integer}> as Iterator>::Item,)`
note: required by a bound in `find`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
--> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:33
|
LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
| ^^^^^^ expected `&&i32`, found integer

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
13 changes: 10 additions & 3 deletions tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
//@ run-rustfix
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@[current] run-rustfix
fn main() {
let _ = (-10..=10).find(|x: i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
let _ = (-10..=10).find(|x: i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected a `FnMut(&<RangeInclusive<{integer}> as Iterator>::Item)` closure, found
let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
//[current]~^ ERROR type mismatch in closure arguments
//[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//@ compile-flags: -Znext-solver

// When encountering a fulfillment error from an `alias-relate` goal failing, we
// would previously manually construct a `normalizes-to` goal involving the alias
// and an infer var. This would then ICE as normalization would return a nested
// goal (the `T: Sized` from the `Trait` impl for `Foo<T>` below) from the root goal
// which is not supported.

struct Foo<T>(T);

trait Trait {
type Assoc;
}

// `T: Sized` being explicit is not required, but the bound being present *is*.
impl<T: Sized> Trait for Foo<T> {
type Assoc = u64;
}

fn bar<T: Trait<Assoc = u32>>(_: T) {}

fn main() {
let foo = Foo(Default::default());
bar(foo);
//~^ ERROR: type mismatch resolving `<Foo<_> as Trait>::Assoc == u32`
// Here diagnostics would manually construct a `<Foo<?y> as Trait>::Assoc normalizes-to ?x` goal
// which would return a nested goal of `?y: Sized` from the impl.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0271]: type mismatch resolving `<Foo<_> as Trait>::Assoc == u32`
--> $DIR/alias_relate_error_uses_structurally_normalize.rs:24:9
|
LL | bar(foo);
| --- ^^^ expected `u32`, found `u64`
| |
| required by a bound introduced by this call
|
note: required by a bound in `bar`
--> $DIR/alias_relate_error_uses_structurally_normalize.rs:20:17
|
LL | fn bar<T: Trait<Assoc = u32>>(_: T) {}
| ^^^^^^^^^^^ required by this bound in `bar`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0271`.