-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Hack out effects support for old solver #132119
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
use rustc_hir as hir; | ||
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt}; | ||
use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation}; | ||
use rustc_middle::ty::fast_reject::DeepRejectCtxt; | ||
use rustc_middle::{span_bug, ty}; | ||
use rustc_type_ir::solve::NoSolution; | ||
use thin_vec::ThinVec; | ||
|
||
use super::SelectionContext; | ||
|
||
pub type HostEffectObligation<'tcx> = Obligation<'tcx, ty::HostEffectPredicate<'tcx>>; | ||
|
||
pub enum EvaluationFailure { | ||
Ambiguous, | ||
NoSolution, | ||
} | ||
|
||
pub fn evaluate_host_effect_obligation<'tcx>( | ||
selcx: &mut SelectionContext<'_, 'tcx>, | ||
obligation: &HostEffectObligation<'tcx>, | ||
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | ||
if selcx.infcx.intercrate { | ||
span_bug!( | ||
obligation.cause.span, | ||
"should not select host obligation in old solver in intercrate mode" | ||
); | ||
} | ||
|
||
match evaluate_host_effect_from_bounds(selcx, obligation) { | ||
Ok(result) => return Ok(result), | ||
Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | ||
Err(EvaluationFailure::NoSolution) => {} | ||
} | ||
|
||
match evaluate_host_effect_from_selection_candiate(selcx, obligation) { | ||
Ok(result) => return Ok(result), | ||
Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), | ||
Err(EvaluationFailure::NoSolution) => {} | ||
} | ||
|
||
Err(EvaluationFailure::NoSolution) | ||
} | ||
|
||
fn match_candidate<'tcx>( | ||
infcx: &InferCtxt<'tcx>, | ||
obligation: &HostEffectObligation<'tcx>, | ||
candidate: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>, | ||
) -> Result<ThinVec<PredicateObligation<'tcx>>, NoSolution> { | ||
if !candidate.skip_binder().host.satisfies(obligation.predicate.host) { | ||
return Err(NoSolution); | ||
} | ||
|
||
let candidate = infcx.instantiate_binder_with_fresh_vars( | ||
obligation.cause.span, | ||
BoundRegionConversionTime::HigherRankedType, | ||
candidate, | ||
); | ||
|
||
let mut nested = infcx | ||
.at(&obligation.cause, obligation.param_env) | ||
.eq(DefineOpaqueTypes::Yes, obligation.predicate.trait_ref, candidate.trait_ref)? | ||
.into_obligations(); | ||
|
||
for nested in &mut nested { | ||
nested.set_depth_from_parent(obligation.recursion_depth); | ||
} | ||
|
||
Ok(nested) | ||
} | ||
|
||
fn evaluate_host_effect_from_bounds<'tcx>( | ||
selcx: &mut SelectionContext<'_, 'tcx>, | ||
obligation: &HostEffectObligation<'tcx>, | ||
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | ||
let infcx = selcx.infcx; | ||
let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); | ||
let mut candidate = None; | ||
|
||
for predicate in obligation.param_env.caller_bounds() { | ||
let bound_predicate = predicate.kind(); | ||
if let ty::ClauseKind::HostEffect(data) = predicate.kind().skip_binder() { | ||
let data = bound_predicate.rebind(data); | ||
if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { | ||
continue; | ||
} | ||
|
||
if !drcx.args_may_unify( | ||
obligation.predicate.trait_ref.args, | ||
data.skip_binder().trait_ref.args, | ||
) { | ||
continue; | ||
} | ||
|
||
let is_match = infcx.probe(|_| match_candidate(infcx, obligation, data).is_ok()); | ||
|
||
if is_match { | ||
if candidate.is_some() { | ||
return Err(EvaluationFailure::Ambiguous); | ||
} else { | ||
candidate = Some(data); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if let Some(data) = candidate { | ||
Ok(match_candidate(infcx, obligation, data) | ||
.expect("candidate matched before, so it should match again")) | ||
} else { | ||
Err(EvaluationFailure::NoSolution) | ||
} | ||
} | ||
|
||
fn evaluate_host_effect_from_selection_candiate<'tcx>( | ||
selcx: &mut SelectionContext<'_, 'tcx>, | ||
obligation: &HostEffectObligation<'tcx>, | ||
) -> Result<ThinVec<PredicateObligation<'tcx>>, EvaluationFailure> { | ||
let tcx = selcx.tcx(); | ||
selcx.infcx.commit_if_ok(|_| { | ||
match selcx.select(&obligation.with(tcx, obligation.predicate.trait_ref)) { | ||
Ok(None) => Err(EvaluationFailure::Ambiguous), | ||
Err(_) => Err(EvaluationFailure::NoSolution), | ||
Ok(Some(source)) => match source { | ||
ImplSource::UserDefined(impl_) => { | ||
if tcx.constness(impl_.impl_def_id) != hir::Constness::Const { | ||
return Err(EvaluationFailure::NoSolution); | ||
} | ||
|
||
let mut nested = impl_.nested; | ||
nested.extend( | ||
tcx.const_conditions(impl_.impl_def_id) | ||
.instantiate(tcx, impl_.args) | ||
.into_iter() | ||
.map(|(trait_ref, _)| { | ||
obligation.with( | ||
tcx, | ||
trait_ref.to_host_effect_clause(tcx, obligation.predicate.host), | ||
) | ||
}), | ||
); | ||
|
||
for nested in &mut nested { | ||
nested.set_depth_from_parent(obligation.recursion_depth); | ||
} | ||
|
||
Ok(nested) | ||
} | ||
_ => Err(EvaluationFailure::NoSolution), | ||
}, | ||
} | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,7 +49,7 @@ use crate::infer::{InferCtxt, InferOk, TypeFreshener}; | |
use crate::solve::InferCtxtSelectExt as _; | ||
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to}; | ||
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt}; | ||
use crate::traits::{ProjectionCacheKey, Unimplemented}; | ||
use crate::traits::{ProjectionCacheKey, Unimplemented, effects}; | ||
|
||
mod _match; | ||
mod candidate_assembly; | ||
|
@@ -645,11 +645,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | |
self.evaluate_trait_predicate_recursively(previous_stack, obligation) | ||
} | ||
|
||
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) => { | ||
// FIXME(effects): It should be relatively straightforward to implement | ||
// old trait solver support for `HostEffect` bounds; or at least basic | ||
// support for them. | ||
todo!() | ||
ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(data)) => { | ||
self.infcx.enter_forall(bound_predicate.rebind(data), |data| { | ||
match effects::evaluate_host_effect_obligation( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pretty sure that can cause undetected overflow 🤔 too tired to explain my thoughts here via comment for today, can do so in sync or maybe you're able to come up with a test yourself There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, though it's the same way that we can encounter overflow in projection afaict. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a consequence of not tracking effect goals (or projections) on the trait stack. |
||
self, | ||
&obligation.with(self.tcx(), data), | ||
) { | ||
Ok(nested) => { | ||
self.evaluate_predicates_recursively(previous_stack, nested) | ||
} | ||
Err(effects::EvaluationFailure::Ambiguous) => Ok(EvaluatedToAmbig), | ||
Err(effects::EvaluationFailure::NoSolution) => Ok(EvaluatedToErr), | ||
} | ||
}) | ||
} | ||
|
||
ty::PredicateKind::Subtype(p) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,15 @@ | ||
error: using `#![feature(effects)]` without enabling next trait solver globally | ||
| | ||
= note: the next trait solver must be enabled globally for the effects feature to work correctly | ||
= help: use `-Znext-solver` to enable | ||
|
||
error[E0080]: evaluation of `foo::<()>::{constant#0}` failed | ||
error[E0277]: the trait bound `T: const Tr` is not satisfied | ||
--> $DIR/constifconst-call-in-const-position.rs:17:38 | ||
| | ||
LL | const fn foo<T: ~const Tr>() -> [u8; T::a()] { | ||
| ^^^^^^ calling non-const function `<() as Tr>::a` | ||
| ^^^^^^ | ||
|
||
error[E0277]: the trait bound `T: const Tr` is not satisfied | ||
--> $DIR/constifconst-call-in-const-position.rs:18:9 | ||
| | ||
LL | [0; T::a()] | ||
| ^^^^^^ | ||
|
||
error: aborting due to 2 previous errors | ||
|
||
For more information about this error, try `rustc --explain E0080`. | ||
For more information about this error, try `rustc --explain E0277`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Increment the depth correctly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did this more manually. It's kinda ugly but 🤷