Rollup merge of #123363 - lcnr:normalizes-to-zero-to-inf, r=BoxyUwU
change `NormalizesTo` to fully structurally normalize notes in https://hackmd.io/wZ016dE4QKGIhrOnHLlThQ need to also update the dev-guide once this PR lands. in short, the setup is now as follows: `normalizes-to` internally implements one step normalization, applying that normalization to the `goal.predicate.term` causes the projected term to get recursively normalized. With this `normalizes-to` normalizes until the projected term is rigid, meaning that we normalize as many steps necessary, but at least 1. To handle rigid aliases, we add another candidate only if the 1 to inf step normalization failed. With this `normalizes-to` is now full structural normalization. We can now change `AliasRelate` to simply emit `normalizes-to` goals for the rhs and lhs. This avoids the concerns from https://github.com/rust-lang/trait-system-refactor-initiative/issues/103 and generally feels cleaner
This commit is contained in:
commit
fcb0e9d07a
15 changed files with 175 additions and 311 deletions
|
@ -121,8 +121,6 @@ pub enum ProbeStep<'tcx> {
|
||||||
/// used whenever there are multiple candidates to prove the
|
/// used whenever there are multiple candidates to prove the
|
||||||
/// current goalby .
|
/// current goalby .
|
||||||
NestedProbe(Probe<'tcx>),
|
NestedProbe(Probe<'tcx>),
|
||||||
CommitIfOkStart,
|
|
||||||
CommitIfOkSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What kind of probe we're in. In case the probe represents a candidate, or
|
/// What kind of probe we're in. In case the probe represents a candidate, or
|
||||||
|
@ -132,6 +130,8 @@ pub enum ProbeStep<'tcx> {
|
||||||
pub enum ProbeKind<'tcx> {
|
pub enum ProbeKind<'tcx> {
|
||||||
/// The root inference context while proving a goal.
|
/// The root inference context while proving a goal.
|
||||||
Root { result: QueryResult<'tcx> },
|
Root { result: QueryResult<'tcx> },
|
||||||
|
/// Trying to normalize an alias by at least one stpe in `NormalizesTo`.
|
||||||
|
TryNormalizeNonRigid { result: QueryResult<'tcx> },
|
||||||
/// Probe entered when normalizing the self ty during candidate assembly
|
/// Probe entered when normalizing the self ty during candidate assembly
|
||||||
NormalizedSelfTyAssembly,
|
NormalizedSelfTyAssembly,
|
||||||
/// Some candidate to prove the current goal.
|
/// Some candidate to prove the current goal.
|
||||||
|
@ -143,9 +143,6 @@ pub enum ProbeKind<'tcx> {
|
||||||
/// Used in the probe that wraps normalizing the non-self type for the unsize
|
/// Used in the probe that wraps normalizing the non-self type for the unsize
|
||||||
/// trait, which is also structurally matched on.
|
/// trait, which is also structurally matched on.
|
||||||
UnsizeAssembly,
|
UnsizeAssembly,
|
||||||
/// A call to `EvalCtxt::commit_if_ok` which failed, causing the work
|
|
||||||
/// to be discarded.
|
|
||||||
CommitIfOk,
|
|
||||||
/// During upcasting from some source object to target object type, used to
|
/// During upcasting from some source object to target object type, used to
|
||||||
/// do a probe to find out what projection type(s) may be used to prove that
|
/// do a probe to find out what projection type(s) may be used to prove that
|
||||||
/// the source type upholds all of the target type's object bounds.
|
/// the source type upholds all of the target type's object bounds.
|
||||||
|
|
|
@ -100,6 +100,9 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||||
ProbeKind::Root { result } => {
|
ProbeKind::Root { result } => {
|
||||||
write!(self.f, "ROOT RESULT: {result:?}")
|
write!(self.f, "ROOT RESULT: {result:?}")
|
||||||
}
|
}
|
||||||
|
ProbeKind::TryNormalizeNonRigid { result } => {
|
||||||
|
write!(self.f, "TRY NORMALIZE NON-RIGID: {result:?}")
|
||||||
|
}
|
||||||
ProbeKind::NormalizedSelfTyAssembly => {
|
ProbeKind::NormalizedSelfTyAssembly => {
|
||||||
write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
write!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
|
||||||
}
|
}
|
||||||
|
@ -109,9 +112,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||||
ProbeKind::UpcastProjectionCompatibility => {
|
ProbeKind::UpcastProjectionCompatibility => {
|
||||||
write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
|
write!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
|
||||||
}
|
}
|
||||||
ProbeKind::CommitIfOk => {
|
|
||||||
write!(self.f, "COMMIT_IF_OK:")
|
|
||||||
}
|
|
||||||
ProbeKind::MiscCandidate { name, result } => {
|
ProbeKind::MiscCandidate { name, result } => {
|
||||||
write!(self.f, "CANDIDATE {name}: {result:?}")
|
write!(self.f, "CANDIDATE {name}: {result:?}")
|
||||||
}
|
}
|
||||||
|
@ -132,8 +132,6 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||||
}
|
}
|
||||||
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
|
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
|
||||||
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
|
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
|
||||||
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,
|
|
||||||
ProbeStep::CommitIfOkSuccess => writeln!(this.f, "COMMIT_IF_OK SUCCESS")?,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
//! Doing this via a separate goal is called "deferred alias relation" and part
|
//! Doing this via a separate goal is called "deferred alias relation" and part
|
||||||
//! of our more general approach to "lazy normalization".
|
//! of our more general approach to "lazy normalization".
|
||||||
//!
|
//!
|
||||||
//! This is done by first normalizing both sides of the goal, ending up in
|
//! This is done by first structurally normalizing both sides of the goal, ending
|
||||||
//! either a concrete type, rigid alias, or an infer variable.
|
//! up in either a concrete type, rigid alias, or an infer variable.
|
||||||
//! These are related further according to the rules below:
|
//! These are related further according to the rules below:
|
||||||
//!
|
//!
|
||||||
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
|
//! (1.) If we end up with two rigid aliases, then we relate them structurally.
|
||||||
|
@ -14,18 +14,10 @@
|
||||||
//!
|
//!
|
||||||
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
|
||||||
//! relate them structurally.
|
//! relate them structurally.
|
||||||
//!
|
|
||||||
//! Subtle: when relating an opaque to another type, we emit a
|
|
||||||
//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
|
|
||||||
//! This nested goal starts out as ambiguous and does not actually define the opaque.
|
|
||||||
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
|
|
||||||
//! `NormalizesTo` goal, at which point the opaque is actually defined.
|
|
||||||
|
|
||||||
use super::EvalCtxt;
|
use super::EvalCtxt;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
|
||||||
use rustc_infer::traits::solve::GoalSource;
|
|
||||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty;
|
||||||
|
|
||||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
#[instrument(level = "debug", skip(self), ret)]
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
|
@ -36,21 +28,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
||||||
|
|
||||||
let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
|
// Structurally normalize the lhs.
|
||||||
return self
|
let lhs = if let Some(alias) = lhs.to_alias_ty(self.tcx()) {
|
||||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
let term = self.next_term_infer_of_kind(lhs);
|
||||||
|
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||||
|
term
|
||||||
|
} else {
|
||||||
|
lhs
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
|
// Structurally normalize the rhs.
|
||||||
return self
|
let rhs = if let Some(alias) = rhs.to_alias_ty(self.tcx()) {
|
||||||
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
let term = self.next_term_infer_of_kind(rhs);
|
||||||
|
self.add_normalizes_to_goal(goal.with(tcx, ty::NormalizesTo { alias, term }));
|
||||||
|
term
|
||||||
|
} else {
|
||||||
|
rhs
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Apply the constraints.
|
||||||
|
self.try_evaluate_added_goals()?;
|
||||||
|
let lhs = self.resolve_vars_if_possible(lhs);
|
||||||
|
let rhs = self.resolve_vars_if_possible(rhs);
|
||||||
|
debug!(?lhs, ?rhs);
|
||||||
|
|
||||||
let variance = match direction {
|
let variance = match direction {
|
||||||
ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
|
ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
|
||||||
ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
|
ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
|
||||||
};
|
};
|
||||||
|
|
||||||
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
|
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
self.relate(param_env, lhs, variance, rhs)?;
|
self.relate(param_env, lhs, variance, rhs)?;
|
||||||
|
@ -58,14 +63,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
(Some(alias), None) => {
|
(Some(alias), None) => {
|
||||||
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)
|
self.relate_rigid_alias_non_alias(param_env, alias, variance, rhs)?;
|
||||||
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
|
}
|
||||||
|
(None, Some(alias)) => {
|
||||||
|
self.relate_rigid_alias_non_alias(
|
||||||
|
param_env,
|
||||||
|
alias,
|
||||||
|
variance.xform(ty::Variance::Contravariant),
|
||||||
|
lhs,
|
||||||
|
)?;
|
||||||
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
}
|
}
|
||||||
(None, Some(alias)) => self.relate_rigid_alias_non_alias(
|
|
||||||
param_env,
|
|
||||||
alias,
|
|
||||||
variance.xform(ty::Variance::Contravariant),
|
|
||||||
lhs,
|
|
||||||
),
|
|
||||||
|
|
||||||
(Some(alias_lhs), Some(alias_rhs)) => {
|
(Some(alias_lhs), Some(alias_rhs)) => {
|
||||||
self.relate(param_env, alias_lhs, variance, alias_rhs)?;
|
self.relate(param_env, alias_lhs, variance, alias_rhs)?;
|
||||||
|
@ -73,104 +82,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relate a rigid alias with another type. This is the same as
|
|
||||||
/// an ordinary relate except that we treat the outer most alias
|
|
||||||
/// constructor as rigid.
|
|
||||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
|
||||||
fn relate_rigid_alias_non_alias(
|
|
||||||
&mut self,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
|
||||||
alias: ty::AliasTy<'tcx>,
|
|
||||||
variance: ty::Variance,
|
|
||||||
term: ty::Term<'tcx>,
|
|
||||||
) -> QueryResult<'tcx> {
|
|
||||||
// NOTE: this check is purely an optimization, the structural eq would
|
|
||||||
// always fail if the term is not an inference variable.
|
|
||||||
if term.is_infer() {
|
|
||||||
let tcx = self.tcx();
|
|
||||||
// We need to relate `alias` to `term` treating only the outermost
|
|
||||||
// constructor as rigid, relating any contained generic arguments as
|
|
||||||
// normal. We do this by first structurally equating the `term`
|
|
||||||
// with the alias constructor instantiated with unconstrained infer vars,
|
|
||||||
// and then relate this with the whole `alias`.
|
|
||||||
//
|
|
||||||
// Alternatively we could modify `Equate` for this case by adding another
|
|
||||||
// variant to `StructurallyRelateAliases`.
|
|
||||||
let identity_args = self.fresh_args_for_item(alias.def_id);
|
|
||||||
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
|
||||||
self.eq_structurally_relating_aliases(param_env, term, rigid_ctor.to_ty(tcx).into())?;
|
|
||||||
self.eq(param_env, alias, rigid_ctor)?;
|
|
||||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
|
||||||
} else {
|
|
||||||
Err(NoSolution)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
|
||||||
fn try_normalize_term(
|
|
||||||
&mut self,
|
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
|
||||||
term: ty::Term<'tcx>,
|
|
||||||
) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
|
|
||||||
match term.unpack() {
|
|
||||||
ty::TermKind::Ty(ty) => {
|
|
||||||
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()) {
|
|
||||||
let term = self.next_term_infer_of_kind(term);
|
|
||||||
self.add_normalizes_to_goal(Goal::new(
|
|
||||||
self.tcx(),
|
|
||||||
param_env,
|
|
||||||
ty::NormalizesTo { alias, term },
|
|
||||||
));
|
|
||||||
self.try_evaluate_added_goals()?;
|
|
||||||
Ok(Some(self.resolve_vars_if_possible(term)))
|
|
||||||
} else {
|
|
||||||
Ok(Some(term))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self, param_env), ret)]
|
|
||||||
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(kind, alias) = *ty.kind() else {
|
|
||||||
return Some(ty);
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.commit_if_ok(|this| {
|
|
||||||
let tcx = this.tcx();
|
|
||||||
let normalized_ty = this.next_ty_infer();
|
|
||||||
let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
|
|
||||||
match kind {
|
|
||||||
ty::AliasKind::Opaque => {
|
|
||||||
// HACK: Unlike for associated types, `normalizes-to` for opaques
|
|
||||||
// is currently not treated as a function. We do not erase the
|
|
||||||
// expected term.
|
|
||||||
this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
|
|
||||||
}
|
|
||||||
ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
|
|
||||||
this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.try_evaluate_added_goals()?;
|
|
||||||
Ok(this.resolve_vars_if_possible(normalized_ty))
|
|
||||||
}) {
|
|
||||||
Ok(ty) => self.try_normalize_ty_recur(param_env, depth + 1, ty),
|
|
||||||
Err(NoSolution) => Some(ty),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,7 +332,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
/// whether an alias is rigid by using the trait solver. When instantiating a response
|
/// whether an alias is rigid by using the trait solver. When instantiating a response
|
||||||
/// from the solver we assume that the solver correctly handled aliases and therefore
|
/// from the solver we assume that the solver correctly handled aliases and therefore
|
||||||
/// always relate them structurally here.
|
/// always relate them structurally here.
|
||||||
#[instrument(level = "debug", skip(infcx), ret)]
|
#[instrument(level = "debug", skip(infcx))]
|
||||||
fn unify_query_var_values(
|
fn unify_query_var_values(
|
||||||
infcx: &InferCtxt<'tcx>,
|
infcx: &InferCtxt<'tcx>,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
use super::{EvalCtxt, NestedGoals};
|
|
||||||
use crate::solve::inspect;
|
|
||||||
use rustc_middle::traits::query::NoSolution;
|
|
||||||
|
|
||||||
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
|
||||||
pub(in crate::solve) fn commit_if_ok<T>(
|
|
||||||
&mut self,
|
|
||||||
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>,
|
|
||||||
) -> Result<T, NoSolution> {
|
|
||||||
let mut nested_ecx = EvalCtxt {
|
|
||||||
infcx: self.infcx,
|
|
||||||
variables: self.variables,
|
|
||||||
var_values: self.var_values,
|
|
||||||
is_normalizes_to_goal: self.is_normalizes_to_goal,
|
|
||||||
predefined_opaques_in_body: self.predefined_opaques_in_body,
|
|
||||||
max_input_universe: self.max_input_universe,
|
|
||||||
search_graph: self.search_graph,
|
|
||||||
nested_goals: NestedGoals::new(),
|
|
||||||
tainted: self.tainted,
|
|
||||||
inspect: self.inspect.new_probe(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx));
|
|
||||||
if result.is_ok() {
|
|
||||||
let EvalCtxt {
|
|
||||||
infcx: _,
|
|
||||||
variables: _,
|
|
||||||
var_values: _,
|
|
||||||
is_normalizes_to_goal: _,
|
|
||||||
predefined_opaques_in_body: _,
|
|
||||||
max_input_universe: _,
|
|
||||||
search_graph: _,
|
|
||||||
nested_goals,
|
|
||||||
tainted,
|
|
||||||
inspect,
|
|
||||||
} = nested_ecx;
|
|
||||||
self.nested_goals.extend(nested_goals);
|
|
||||||
self.tainted = tainted;
|
|
||||||
self.inspect.integrate_snapshot(inspect);
|
|
||||||
} else {
|
|
||||||
nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk);
|
|
||||||
self.inspect.finish_probe(nested_ecx.inspect);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -24,7 +24,6 @@ use rustc_middle::ty::{
|
||||||
use rustc_session::config::DumpSolverProofTree;
|
use rustc_session::config::DumpSolverProofTree;
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::iter;
|
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||||
|
@ -36,7 +35,6 @@ use super::{GoalSource, SolverMode};
|
||||||
pub use select::InferCtxtSelectExt;
|
pub use select::InferCtxtSelectExt;
|
||||||
|
|
||||||
mod canonical;
|
mod canonical;
|
||||||
mod commit_if_ok;
|
|
||||||
mod probe;
|
mod probe;
|
||||||
mod select;
|
mod select;
|
||||||
|
|
||||||
|
@ -124,11 +122,6 @@ impl<'tcx> NestedGoals<'tcx> {
|
||||||
pub(super) fn is_empty(&self) -> bool {
|
pub(super) fn is_empty(&self) -> bool {
|
||||||
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
|
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
|
|
||||||
self.normalizes_to_goals.extend(other.normalizes_to_goals);
|
|
||||||
self.goals.extend(other.goals)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||||
|
@ -511,12 +504,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
self.inspect.evaluate_added_goals_loop_start();
|
self.inspect.evaluate_added_goals_loop_start();
|
||||||
|
|
||||||
fn with_misc_source<'tcx>(
|
|
||||||
it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
|
||||||
) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> {
|
|
||||||
iter::zip(iter::repeat(GoalSource::Misc), it)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this loop did not result in any progress, what's our final certainty.
|
// If this loop did not result in any progress, what's our final certainty.
|
||||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
let mut unchanged_certainty = Some(Certainty::Yes);
|
||||||
for goal in goals.normalizes_to_goals {
|
for goal in goals.normalizes_to_goals {
|
||||||
|
@ -534,16 +521,28 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
unconstrained_goal,
|
unconstrained_goal,
|
||||||
)?;
|
)?;
|
||||||
// Add the nested goals from normalization to our own nested goals.
|
// Add the nested goals from normalization to our own nested goals.
|
||||||
|
debug!(?nested_goals);
|
||||||
goals.goals.extend(nested_goals);
|
goals.goals.extend(nested_goals);
|
||||||
|
|
||||||
// Finally, equate the goal's RHS with the unconstrained var.
|
// Finally, equate the goal's RHS with the unconstrained var.
|
||||||
// We put the nested goals from this into goals instead of
|
//
|
||||||
// next_goals to avoid needing to process the loop one extra
|
// SUBTLE:
|
||||||
// time if this goal returns something -- I don't think this
|
// We structurally relate aliases here. This is necessary
|
||||||
// matters in practice, though.
|
// as we otherwise emit a nested `AliasRelate` goal in case the
|
||||||
let eq_goals =
|
// returned term is a rigid alias, resulting in overflow.
|
||||||
self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?;
|
//
|
||||||
goals.goals.extend(with_misc_source(eq_goals));
|
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
|
||||||
|
// start out as an unconstrained inference variable so any aliases get
|
||||||
|
// fully normalized when instantiating it.
|
||||||
|
//
|
||||||
|
// FIXME: Strictly speaking this may be incomplete if the normalized-to
|
||||||
|
// type contains an ambiguous alias referencing bound regions. We should
|
||||||
|
// consider changing this to only use "shallow structural equality".
|
||||||
|
self.eq_structurally_relating_aliases(
|
||||||
|
goal.param_env,
|
||||||
|
goal.predicate.term,
|
||||||
|
unconstrained_rhs,
|
||||||
|
)?;
|
||||||
|
|
||||||
// We only look at the `projection_ty` part here rather than
|
// We only look at the `projection_ty` part here rather than
|
||||||
// looking at the "has changed" return from evaluate_goal,
|
// looking at the "has changed" return from evaluate_goal,
|
||||||
|
@ -731,6 +730,46 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This should be used when relating a rigid alias with another type.
|
||||||
|
///
|
||||||
|
/// Normally we emit a nested `AliasRelate` when equating an inference
|
||||||
|
/// variable and an alias. This causes us to instead constrain the inference
|
||||||
|
/// variable to the alias without emitting a nested alias relate goals.
|
||||||
|
#[instrument(level = "debug", skip(self, param_env), ret)]
|
||||||
|
pub(super) fn relate_rigid_alias_non_alias(
|
||||||
|
&mut self,
|
||||||
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
|
alias: ty::AliasTy<'tcx>,
|
||||||
|
variance: ty::Variance,
|
||||||
|
term: ty::Term<'tcx>,
|
||||||
|
) -> Result<(), NoSolution> {
|
||||||
|
// NOTE: this check is purely an optimization, the structural eq would
|
||||||
|
// always fail if the term is not an inference variable.
|
||||||
|
if term.is_infer() {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
// We need to relate `alias` to `term` treating only the outermost
|
||||||
|
// constructor as rigid, relating any contained generic arguments as
|
||||||
|
// normal. We do this by first structurally equating the `term`
|
||||||
|
// with the alias constructor instantiated with unconstrained infer vars,
|
||||||
|
// and then relate this with the whole `alias`.
|
||||||
|
//
|
||||||
|
// Alternatively we could modify `Equate` for this case by adding another
|
||||||
|
// variant to `StructurallyRelateAliases`.
|
||||||
|
let identity_args = self.fresh_args_for_item(alias.def_id);
|
||||||
|
let rigid_ctor = ty::AliasTy::new(tcx, alias.def_id, identity_args);
|
||||||
|
let ctor_ty = rigid_ctor.to_ty(tcx);
|
||||||
|
let InferOk { value: (), obligations } = self
|
||||||
|
.infcx
|
||||||
|
.at(&ObligationCause::dummy(), param_env)
|
||||||
|
.trace(term, ctor_ty.into())
|
||||||
|
.eq_structurally_relating_aliases(term, ctor_ty.into())?;
|
||||||
|
debug_assert!(obligations.is_empty());
|
||||||
|
self.relate(param_env, alias, variance, rigid_ctor)
|
||||||
|
} else {
|
||||||
|
Err(NoSolution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This sohuld only be used when we're either instantiating a previously
|
/// This sohuld only be used when we're either instantiating a previously
|
||||||
/// unconstrained "return value" or when we're sure that all aliases in
|
/// unconstrained "return value" or when we're sure that all aliases in
|
||||||
/// the types are rigid.
|
/// the types are rigid.
|
||||||
|
|
|
@ -130,17 +130,14 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
||||||
self.candidates_recur(candidates, nested_goals, probe);
|
self.candidates_recur(candidates, nested_goals, probe);
|
||||||
nested_goals.truncate(num_goals);
|
nested_goals.truncate(num_goals);
|
||||||
}
|
}
|
||||||
inspect::ProbeStep::EvaluateGoals(_)
|
inspect::ProbeStep::EvaluateGoals(_) => (),
|
||||||
| inspect::ProbeStep::CommitIfOkStart
|
|
||||||
| inspect::ProbeStep::CommitIfOkSuccess => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match probe.kind {
|
match probe.kind {
|
||||||
inspect::ProbeKind::NormalizedSelfTyAssembly
|
inspect::ProbeKind::NormalizedSelfTyAssembly
|
||||||
| inspect::ProbeKind::UnsizeAssembly
|
| inspect::ProbeKind::UnsizeAssembly
|
||||||
| inspect::ProbeKind::UpcastProjectionCompatibility
|
| inspect::ProbeKind::UpcastProjectionCompatibility => (),
|
||||||
| inspect::ProbeKind::CommitIfOk => (),
|
|
||||||
// We add a candidate for the root evaluation if there
|
// We add a candidate for the root evaluation if there
|
||||||
// is only one way to prove a given goal, e.g. for `WellFormed`.
|
// is only one way to prove a given goal, e.g. for `WellFormed`.
|
||||||
//
|
//
|
||||||
|
@ -157,7 +154,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inspect::ProbeKind::MiscCandidate { name: _, result }
|
inspect::ProbeKind::TryNormalizeNonRigid { result }
|
||||||
|
| inspect::ProbeKind::MiscCandidate { name: _, result }
|
||||||
| inspect::ProbeKind::TraitCandidate { source: _, result } => {
|
| inspect::ProbeKind::TraitCandidate { source: _, result } => {
|
||||||
candidates.push(InspectCandidate {
|
candidates.push(InspectCandidate {
|
||||||
goal: self,
|
goal: self,
|
||||||
|
|
|
@ -220,8 +220,6 @@ enum WipProbeStep<'tcx> {
|
||||||
AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
||||||
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
|
EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
|
||||||
NestedProbe(WipProbe<'tcx>),
|
NestedProbe(WipProbe<'tcx>),
|
||||||
CommitIfOkStart,
|
|
||||||
CommitIfOkSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> WipProbeStep<'tcx> {
|
impl<'tcx> WipProbeStep<'tcx> {
|
||||||
|
@ -230,8 +228,6 @@ impl<'tcx> WipProbeStep<'tcx> {
|
||||||
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
|
WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal),
|
||||||
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
|
WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
|
||||||
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
|
WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
|
||||||
WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart,
|
|
||||||
WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -467,29 +463,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside
|
|
||||||
/// of the probe into the parent.
|
|
||||||
pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) {
|
|
||||||
if let Some(this) = self.as_mut() {
|
|
||||||
match (this, *probe.state.unwrap()) {
|
|
||||||
(
|
|
||||||
DebugSolver::Probe(WipProbe { steps, .. })
|
|
||||||
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
|
||||||
evaluation: WipProbe { steps, .. },
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
DebugSolver::Probe(probe),
|
|
||||||
) => {
|
|
||||||
steps.push(WipProbeStep::CommitIfOkStart);
|
|
||||||
assert_eq!(probe.kind, None);
|
|
||||||
steps.extend(probe.steps);
|
|
||||||
steps.push(WipProbeStep::CommitIfOkSuccess);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
|
pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> {
|
||||||
self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
|
self.nested(|| WipAddedGoalsEvaluation { evaluations: vec![], result: None })
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
|
use rustc_infer::traits::solve::inspect::ProbeKind;
|
||||||
use rustc_infer::traits::specialization_graph::LeafDef;
|
use rustc_infer::traits::specialization_graph::LeafDef;
|
||||||
use rustc_infer::traits::Reveal;
|
use rustc_infer::traits::Reveal;
|
||||||
use rustc_middle::traits::solve::{
|
use rustc_middle::traits::solve::{
|
||||||
|
@ -30,14 +31,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
&mut self,
|
&mut self,
|
||||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let def_id = goal.predicate.def_id();
|
self.set_is_normalizes_to_goal();
|
||||||
let def_kind = self.tcx().def_kind(def_id);
|
|
||||||
match def_kind {
|
|
||||||
DefKind::OpaqueTy => return self.normalize_opaque_type(goal),
|
|
||||||
_ => self.set_is_normalizes_to_goal(),
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert!(self.term_is_fully_unconstrained(goal));
|
debug_assert!(self.term_is_fully_unconstrained(goal));
|
||||||
|
let normalize_result = self
|
||||||
|
.probe(|&result| ProbeKind::TryNormalizeNonRigid { result })
|
||||||
|
.enter(|this| this.normalize_at_least_one_step(goal));
|
||||||
|
|
||||||
|
match normalize_result {
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
Err(NoSolution) => {
|
||||||
|
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
|
||||||
|
if alias.opt_kind(self.tcx()).is_some() {
|
||||||
|
self.relate_rigid_alias_non_alias(
|
||||||
|
param_env,
|
||||||
|
alias,
|
||||||
|
ty::Variance::Invariant,
|
||||||
|
term,
|
||||||
|
)?;
|
||||||
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
|
} else {
|
||||||
|
// FIXME(generic_const_exprs): we currently do not support rigid
|
||||||
|
// unevaluated constants.
|
||||||
|
Err(NoSolution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalize the given alias by at least one step. If the alias is rigid, this
|
||||||
|
/// returns `NoSolution`.
|
||||||
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
|
fn normalize_at_least_one_step(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
let def_id = goal.predicate.def_id();
|
||||||
match self.tcx().def_kind(def_id) {
|
match self.tcx().def_kind(def_id) {
|
||||||
DefKind::AssocTy | DefKind::AssocConst => {
|
DefKind::AssocTy | DefKind::AssocConst => {
|
||||||
match self.tcx().associated_item(def_id).container {
|
match self.tcx().associated_item(def_id).container {
|
||||||
|
@ -52,35 +80,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
DefKind::AnonConst => self.normalize_anon_const(goal),
|
DefKind::AnonConst => self.normalize_anon_const(goal),
|
||||||
DefKind::TyAlias => self.normalize_weak_type(goal),
|
DefKind::TyAlias => self.normalize_weak_type(goal),
|
||||||
|
DefKind::OpaqueTy => self.normalize_opaque_type(goal),
|
||||||
kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)),
|
kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When normalizing an associated item, constrain the result to `term`.
|
/// When normalizing an associated item, constrain the expected term to `term`.
|
||||||
///
|
///
|
||||||
/// While `NormalizesTo` goals have the normalized-to term as an argument,
|
/// We know `term` to always be a fully unconstrained inference variable, so
|
||||||
/// this argument is always fully unconstrained for associated items.
|
/// `eq` should never fail here. However, in case `term` contains aliases, we
|
||||||
/// It is therefore appropriate to instead think of these `NormalizesTo` goals
|
/// emit nested `AliasRelate` goals to structurally normalize the alias.
|
||||||
/// as function returning a term after normalizing.
|
|
||||||
///
|
|
||||||
/// When equating an inference variable and an alias, we tend to emit `alias-relate`
|
|
||||||
/// goals and only actually instantiate the inference variable with an alias if the
|
|
||||||
/// alias is rigid. However, this means that constraining the expected term of
|
|
||||||
/// such goals ends up fully structurally normalizing the resulting type instead of
|
|
||||||
/// only by one step. To avoid this we instead use structural equality here, resulting
|
|
||||||
/// in each `NormalizesTo` only projects by a single step.
|
|
||||||
///
|
|
||||||
/// Not doing so, currently causes issues because trying to normalize an opaque type
|
|
||||||
/// during alias-relate doesn't actually constrain the opaque if the concrete type
|
|
||||||
/// is an inference variable. This means that `NormalizesTo` for associated types
|
|
||||||
/// normalizing to an opaque type always resulted in ambiguity, breaking tests e.g.
|
|
||||||
/// tests/ui/type-alias-impl-trait/issue-78450.rs.
|
|
||||||
pub fn instantiate_normalizes_to_term(
|
pub fn instantiate_normalizes_to_term(
|
||||||
&mut self,
|
&mut self,
|
||||||
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||||
term: ty::Term<'tcx>,
|
term: ty::Term<'tcx>,
|
||||||
) {
|
) {
|
||||||
self.eq_structurally_relating_aliases(goal.param_env, goal.predicate.term, term)
|
self.eq(goal.param_env, goal.predicate.term, term)
|
||||||
.expect("expected goal term to be fully unconstrained");
|
.expect("expected goal term to be fully unconstrained");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Otherwise, define a new opaque type
|
||||||
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
|
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
|
||||||
self.add_item_bounds_for_hidden_type(
|
self.add_item_bounds_for_hidden_type(
|
||||||
|
|
|
@ -6,7 +6,7 @@ LL | cmp_eq
|
||||||
|
|
|
|
||||||
= note: cannot satisfy `_: Scalar`
|
= note: cannot satisfy `_: Scalar`
|
||||||
note: required by a bound in `cmp_eq`
|
note: required by a bound in `cmp_eq`
|
||||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||||
|
|
|
|
||||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||||
| ^^^^^^ required by this bound in `cmp_eq`
|
| ^^^^^^ required by this bound in `cmp_eq`
|
||||||
|
@ -15,34 +15,6 @@ help: consider specifying the generic arguments
|
||||||
LL | cmp_eq::<A, B, O>
|
LL | cmp_eq::<A, B, O>
|
||||||
| +++++++++++
|
| +++++++++++
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
error: aborting due to 1 previous error
|
||||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
|
||||||
|
|
|
||||||
LL | cmp_eq
|
|
||||||
| ^^^^^^
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}`
|
For more information about this error, try `rustc --explain E0283`.
|
||||||
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
|
|
||||||
|
|
|
||||||
LL | cmp_eq
|
|
||||||
| ^^^^^^
|
|
||||||
|
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::<O, ..., ...>} <: ...`
|
|
||||||
--> $DIR/ambig-hr-projection-issue-93340.rs:14:51
|
|
||||||
|
|
|
||||||
LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
|
||||||
| ___________________________________________________^
|
|
||||||
LL | |
|
|
||||||
LL | | cmp_eq
|
|
||||||
LL | |
|
|
||||||
LL | |
|
|
||||||
LL | |
|
|
||||||
LL | | }
|
|
||||||
| |_^
|
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
|
||||||
|
|
||||||
Some errors have detailed explanations: E0275, E0283.
|
|
||||||
For more information about an error, try `rustc --explain E0275`.
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ LL | cmp_eq
|
||||||
|
|
|
|
||||||
= note: cannot satisfy `_: Scalar`
|
= note: cannot satisfy `_: Scalar`
|
||||||
note: required by a bound in `cmp_eq`
|
note: required by a bound in `cmp_eq`
|
||||||
--> $DIR/ambig-hr-projection-issue-93340.rs:9:22
|
--> $DIR/ambig-hr-projection-issue-93340.rs:10:22
|
||||||
|
|
|
|
||||||
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
|
||||||
| ^^^^^^ required by this bound in `cmp_eq`
|
| ^^^^^^ required by this bound in `cmp_eq`
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//@ revisions: old next
|
//@ revisions: old next
|
||||||
|
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||||
//@[next] compile-flags: -Znext-solver
|
//@[next] compile-flags: -Znext-solver
|
||||||
pub trait Scalar: 'static {
|
pub trait Scalar: 'static {
|
||||||
type RefType<'a>: ScalarRef<'a>;
|
type RefType<'a>: ScalarRef<'a>;
|
||||||
|
@ -12,11 +13,8 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT
|
||||||
|
|
||||||
fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
|
fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
|
||||||
) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
|
||||||
//[next]~^ ERROR overflow evaluating the requirement
|
|
||||||
cmp_eq
|
cmp_eq
|
||||||
//~^ ERROR type annotations needed
|
//~^ ERROR type annotations needed
|
||||||
//[next]~| ERROR overflow evaluating the requirement
|
|
||||||
//[next]~| ERROR overflow evaluating the requirement
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
error[E0282]: type annotations needed
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/recursive-coroutine-boxed.rs:12:23
|
--> $DIR/recursive-coroutine-boxed.rs:14:23
|
||||||
|
|
|
|
||||||
LL | let mut gen = Box::pin(foo());
|
LL | let mut gen = Box::pin(foo());
|
||||||
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
|
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
|
||||||
|
@ -12,12 +12,28 @@ help: consider specifying the generic argument
|
||||||
LL | let mut gen = Box::<T>::pin(foo());
|
LL | let mut gen = Box::<T>::pin(foo());
|
||||||
| +++++
|
| +++++
|
||||||
|
|
||||||
error[E0282]: type annotations needed
|
error[E0308]: mismatched types
|
||||||
--> $DIR/recursive-coroutine-boxed.rs:9:13
|
--> $DIR/recursive-coroutine-boxed.rs:13:5
|
||||||
|
|
|
|
||||||
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
|
| ---------------------------------------
|
||||||
|
| |
|
||||||
|
| the expected opaque type
|
||||||
|
| expected `impl Coroutine<Yield = (), Return = ()>` because of return type
|
||||||
|
...
|
||||||
|
LL | / || {
|
||||||
|
LL | | let mut gen = Box::pin(foo());
|
||||||
|
LL | |
|
||||||
|
LL | | let mut r = gen.as_mut().resume(());
|
||||||
|
... |
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_____^ types differ
|
||||||
|
|
|
||||||
|
= note: expected opaque type `impl Coroutine<Yield = (), Return = ()>`
|
||||||
|
found coroutine `{coroutine@$DIR/recursive-coroutine-boxed.rs:13:5: 13:7}`
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0282`.
|
Some errors have detailed explanations: E0282, E0308.
|
||||||
|
For more information about an error, try `rustc --explain E0282`.
|
||||||
|
|
|
@ -7,8 +7,10 @@
|
||||||
use std::ops::{Coroutine, CoroutineState};
|
use std::ops::{Coroutine, CoroutineState};
|
||||||
|
|
||||||
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
|
||||||
//[next]~^ ERROR type annotations needed
|
// FIXME(-Znext-solver): this fails with a mismatched types as the
|
||||||
|| {
|
// hidden type of the opaque ends up as {type error}. We should not
|
||||||
|
// emit errors for such goals.
|
||||||
|
|| { //[next]~ ERROR mismatched types
|
||||||
let mut gen = Box::pin(foo());
|
let mut gen = Box::pin(foo());
|
||||||
//[next]~^ ERROR type annotations needed
|
//[next]~^ ERROR type annotations needed
|
||||||
let mut r = gen.as_mut().resume(());
|
let mut r = gen.as_mut().resume(());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue