Skip to content

Commit

Permalink
Use structurally_normalize instead of manual normalizes-to goals
Browse files Browse the repository at this point in the history
  • Loading branch information
BoxyUwU committed Jan 22, 2025
1 parent b2728d5 commit 513bfaa
Show file tree
Hide file tree
Showing 21 changed files with 254 additions and 108 deletions.
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
Expand Up @@ -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
Expand All @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_trait_selection/src/traits/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}
71 changes: 24 additions & 47 deletions compiler/rustc_trait_selection/src/traits/structural_normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,37 @@ impl<'tcx> At<'_, 'tcx> {
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
Expand All @@ -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);
Expand All @@ -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
Expand Up @@ -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
Expand Down
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`
Expand Down
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>
| -- ----
Expand All @@ -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
Expand Down
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

Expand All @@ -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
Expand All @@ -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 `(&_, &_)`
Expand Down
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`.
Loading

0 comments on commit 513bfaa

Please sign in to comment.