Skip to content

Commit

Permalink
use alias-relate to structurally normalize in the solver
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Feb 2, 2024
1 parent b4e53c0 commit fbd1a87
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 108 deletions.
41 changes: 34 additions & 7 deletions compiler/rustc_trait_selection/src/solve/alias_relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,9 @@
//! `NormalizesTo` goal, at which point the opaque is actually defined.

use super::{EvalCtxt, GoalSource};
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::query::NoSolution;
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::ty;
use rustc_middle::ty::{self, Ty};

impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
Expand Down Expand Up @@ -79,7 +78,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}

// FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
/// Normalize the `term` to equate it later. This does not define opaque types.
/// Normalize the `term` to equate it later.
#[instrument(level = "debug", skip(self, param_env), ret)]
fn try_normalize_term(
&mut self,
Expand All @@ -88,10 +87,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
match term.unpack() {
ty::TermKind::Ty(ty) => {
// We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`.
Ok(self
.try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty)
.map(Into::into))
Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into))
}
ty::TermKind::Const(_) => {
if let Some(alias) = term.to_alias_ty(self.tcx()) {
Expand All @@ -108,4 +104,35 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
}

fn try_normalize_ty_recur(
&mut self,
param_env: ty::ParamEnv<'tcx>,
depth: usize,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
if !self.tcx().recursion_limit().value_within_limit(depth) {
return None;
}

let ty::Alias(_, alias) = *ty.kind() else {
return Some(ty);
};

match self.commit_if_ok(|this| {
let normalized_ty = this.next_ty_infer();
let normalizes_to_goal = Goal::new(
this.tcx(),
param_env,
ty::NormalizesTo { alias, term: normalized_ty.into() },
);
this.add_goal(GoalSource::Misc, normalizes_to_goal);
this.try_evaluate_added_goals()?;
let ty = this.resolve_vars_if_possible(normalized_ty);
Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty))
}) {
Ok(ty) => ty,
Err(NoSolution) => Some(ty),
}
}
}
24 changes: 7 additions & 17 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
vec![Candidate { source, result }]
};

let Some(normalized_self_ty) =
self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
let Ok(normalized_self_ty) =
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
else {
debug!("overflow while evaluating self type");
return dummy_candidate(self, Certainty::OVERFLOW);
return vec![];
};

if normalized_self_ty.is_ty_var() {
debug!("self type has been normalized to infer");
return dummy_candidate(self, Certainty::AMBIGUOUS);
Expand Down Expand Up @@ -770,19 +768,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {

let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
let trait_ref = goal.predicate.trait_ref(tcx);
#[derive(Debug)]
struct Overflow;
let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
Some(ty) => Ok(ty),
None => Err(Overflow),
};
let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty);

match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
Err(Overflow) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
}
Ok(Ok(())) => Err(NoSolution),
Ok(Err(_)) => {
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? {
Ok(()) => Err(NoSolution),
Err(_) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
Expand Down
56 changes: 19 additions & 37 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
//! about it on zulip.
use rustc_hir::def_id::DefId;
use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::query::NoSolution;
use rustc_middle::infer::canonical::CanonicalVarInfos;
use rustc_middle::traits::solve::{
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
QueryResult, Response,
};
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
};
Expand Down Expand Up @@ -285,49 +284,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
}

/// Normalize a type when it is structually matched on.
/// Normalize a type for when it is structurally matched on.
///
/// In nearly all cases this function must be used before matching on a type.
/// This function is necessary in nearly all cases before matching on a type.
/// Not doing so is likely to be incomplete and therefore unsound during
/// coherence.
#[instrument(level = "debug", skip(self), ret)]
fn try_normalize_ty(
&mut self,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty)
}

fn try_normalize_ty_recur(
fn structurally_normalize_ty(
&mut self,
param_env: ty::ParamEnv<'tcx>,
define_opaque_types: DefineOpaqueTypes,
depth: usize,
ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
if !self.tcx().recursion_limit().value_within_limit(depth) {
return None;
}

let ty::Alias(_, alias) = *ty.kind() else {
return Some(ty);
};

match self.commit_if_ok(|this| {
let normalized_ty = this.next_ty_infer();
let normalizes_to_goal = Goal::new(
this.tcx(),
) -> Result<Ty<'tcx>, NoSolution> {
if let ty::Alias(..) = ty.kind() {
let normalized_ty = self.next_ty_infer();
let alias_relate_goal = Goal::new(
self.tcx(),
param_env,
ty::NormalizesTo { alias, term: normalized_ty.into() },
ty::PredicateKind::AliasRelate(
ty.into(),
normalized_ty.into(),
AliasRelationDirection::Equate,
),
);
this.add_goal(GoalSource::Misc, normalizes_to_goal);
this.try_evaluate_added_goals()?;
let ty = this.resolve_vars_if_possible(normalized_ty);
Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty))
}) {
Ok(ty) => ty,
Err(NoSolution) => Some(ty),
self.add_goal(GoalSource::Misc, alias_relate_goal);
self.try_evaluate_added_goals()?;
Ok(self.resolve_vars_if_possible(normalized_ty))
} else {
Ok(ty)
}
}
}
Expand Down
20 changes: 5 additions & 15 deletions compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,21 +58,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}

let expected = match self.try_normalize_ty(goal.param_env, expected) {
Some(ty) => {
if ty.is_ty_var() {
return self.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
);
} else {
ty
}
}
None => {
return self
.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
}
};
let expected = self.structurally_normalize_ty(goal.param_env, expected)?;
if expected.is_ty_var() {
return self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
}

// Otherwise, define a new opaque type
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_trait_selection/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
let a_ty = goal.predicate.self_ty();
// We need to normalize the b_ty since it's matched structurally
// in the other functions below.
let b_ty = match ecx
.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
{
Some(b_ty) => b_ty,
None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
let Ok(b_ty) = ecx.structurally_normalize_ty(
goal.param_env,
goal.predicate.trait_ref.args.type_at(1),
) else {
return vec![];
};

let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
Expand Down
17 changes: 17 additions & 0 deletions tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0282]: type annotations needed
--> $DIR/recursive-coroutine-boxed.rs:10:23
|
LL | let mut gen = Box::pin(foo());
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
LL |
LL | let mut r = gen.as_mut().resume(());
| ------ type must be known at this point
|
help: consider specifying the generic argument
|
LL | let mut gen = Box::<T>::pin(foo());
| +++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.
3 changes: 2 additions & 1 deletion tests/ui/impl-trait/recursive-coroutine-boxed.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// check-pass
// revisions: current next
//[current] check-pass
//[next] compile-flags: -Znext-solver
#![feature(coroutines, coroutine_trait)]

Expand All @@ -8,6 +8,7 @@ use std::ops::{Coroutine, CoroutineState};
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|| {
let mut gen = Box::pin(foo());
//[next]~^ ERROR type annotations needed
let mut r = gen.as_mut().resume(());
while let CoroutineState::Yielded(v) = r {
yield v;
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0284]: type annotations needed: cannot satisfy `A <: B`
error[E0284]: type annotations needed: cannot satisfy `A == B`
--> $DIR/two_tait_defining_each_other2.rs:11:5
|
LL | x // B's hidden type is A (opaquely)
| ^ cannot satisfy `A <: B`
| ^ cannot satisfy `A == B`

error: aborting due to 1 previous error

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/impl-trait/two_tait_defining_each_other2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait Foo {}
fn muh(x: A) -> B {
x // B's hidden type is A (opaquely)
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
//[next]~^^ ERROR type annotations needed: cannot satisfy `A <: B`
//[next]~^^ ERROR type annotations needed: cannot satisfy `A == B`
}

struct Bar;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// compile-flags: -Znext-solver
// check-pass

// FIXME(-Znext-solver): This test is currently broken because the `deduce_closure_signature`
// is unable to look at nested obligations.
trait Foo {
fn test() -> impl Fn(u32) -> u32 {
|x| x.count_ones()
//~^ ERROR type annotations needed
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0282]: type annotations needed
--> $DIR/deduce-closure-signature-after-normalization.rs:6:10
|
LL | |x| x.count_ones()
| ^ - type must be known at this point
|
help: consider giving this closure parameter an explicit type
|
LL | |x: /* Type */| x.count_ones()
| ++++++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0282`.
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
error[E0284]: type annotations needed: cannot satisfy `Foo == _`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18
|
LL | needs_send::<Foo>();
| ^^^
|
= note: cannot satisfy `Foo: Send`
note: required by a bound in `needs_send`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
|
LL | fn needs_send<T: Send>() {}
| ^^^^ required by this bound in `needs_send`
| ^^^ cannot satisfy `Foo == _`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0284`.
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
error[E0284]: type annotations needed: cannot satisfy `Foo == _`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:15:18
|
LL | needs_send::<Foo>();
| ^^^
|
= note: cannot satisfy `Foo: Send`
note: required by a bound in `needs_send`
--> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
|
LL | fn needs_send<T: Send>() {}
| ^^^^ required by this bound in `needs_send`
| ^^^ cannot satisfy `Foo == _`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0283`.
For more information about this error, try `rustc --explain E0284`.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ fn needs_send<T: Send>() {}

fn test(_: Foo) {
needs_send::<Foo>();
//~^ ERROR type annotations needed: cannot satisfy `Foo: Send`
//~^ ERROR type annotations needed: cannot satisfy `Foo == _`
}

fn defines(_: Foo) {
Expand Down

0 comments on commit fbd1a87

Please sign in to comment.