-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #56384 - scalexm:chalk, r=nikomatsakis
Implement the new-style trait solver Final PR of what I believe to be a minimally working implementation of the new-style trait solver. The new trait solver can be used by providing the `-Z chalk` command line flag. It is currently used everywhere in `rustc_typeck`, and for everything relying on `rustc::infer::canonical::query_response::enter_canonical_trait_query`. The trait solver is invoked in rustc by using the `evaluate_goal` canonical query. This is not optimal because each call to `evaluate_goal` creates a new `chalk_engine::Forest`, hence rustc cannot use answers to intermediate goals produced by the root goal. We'll need to change that but I guess that's ok for now. Some next steps, I think, are: * handle region constraints: region constraints are computed but are completely ignored for now, I think we may need additional support from `chalk_engine` (as a side effect, types or trait references with outlive requirements cannot be proved well-formed) * deactivate eager normalization in the presence of `-Z chalk` in order to leverage the lazy normalization strategy of the new-style trait solver * add the remaining built-in impls (only `Sized` is supported currently) * transition the compiler to using generic goals instead of predicates that still refer to named type parameters etc I added a few very simple tests to check that the new solver has the right behavior, they won't be needed anymore once it is mature enough. Additionally it shows off that we get [implied bounds](#44491) for free. r? @nikomatsakis
- Loading branch information
Showing
48 changed files
with
1,311 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
use traits::{ | ||
Environment, | ||
InEnvironment, | ||
TraitEngine, | ||
ObligationCause, | ||
PredicateObligation, | ||
FulfillmentError, | ||
FulfillmentErrorCode, | ||
SelectionError, | ||
}; | ||
use traits::query::NoSolution; | ||
use infer::InferCtxt; | ||
use infer::canonical::{Canonical, OriginalQueryValues}; | ||
use ty::{self, Ty}; | ||
use rustc_data_structures::fx::FxHashSet; | ||
|
||
pub type CanonicalGoal<'tcx> = Canonical<'tcx, InEnvironment<'tcx, ty::Predicate<'tcx>>>; | ||
|
||
pub struct FulfillmentContext<'tcx> { | ||
obligations: FxHashSet<InEnvironment<'tcx, PredicateObligation<'tcx>>>, | ||
} | ||
|
||
impl FulfillmentContext<'tcx> { | ||
crate fn new() -> Self { | ||
FulfillmentContext { | ||
obligations: FxHashSet::default(), | ||
} | ||
} | ||
} | ||
|
||
fn in_environment( | ||
infcx: &InferCtxt<'_, 'gcx, 'tcx>, | ||
obligation: PredicateObligation<'tcx> | ||
) -> InEnvironment<'tcx, PredicateObligation<'tcx>> { | ||
assert!(!infcx.is_in_snapshot()); | ||
let obligation = infcx.resolve_type_vars_if_possible(&obligation); | ||
|
||
let environment = match obligation.param_env.def_id { | ||
Some(def_id) => infcx.tcx.environment(def_id), | ||
None if obligation.param_env.caller_bounds.is_empty() => Environment { | ||
clauses: ty::List::empty(), | ||
}, | ||
_ => bug!("non-empty `ParamEnv` with no def-id"), | ||
}; | ||
|
||
InEnvironment { | ||
environment, | ||
goal: obligation, | ||
} | ||
} | ||
|
||
impl TraitEngine<'tcx> for FulfillmentContext<'tcx> { | ||
fn normalize_projection_type( | ||
&mut self, | ||
infcx: &InferCtxt<'_, 'gcx, 'tcx>, | ||
_param_env: ty::ParamEnv<'tcx>, | ||
projection_ty: ty::ProjectionTy<'tcx>, | ||
_cause: ObligationCause<'tcx>, | ||
) -> Ty<'tcx> { | ||
infcx.tcx.mk_ty(ty::Projection(projection_ty)) | ||
} | ||
|
||
fn register_predicate_obligation( | ||
&mut self, | ||
infcx: &InferCtxt<'_, 'gcx, 'tcx>, | ||
obligation: PredicateObligation<'tcx>, | ||
) { | ||
self.obligations.insert(in_environment(infcx, obligation)); | ||
} | ||
|
||
fn select_all_or_error( | ||
&mut self, | ||
infcx: &InferCtxt<'_, 'gcx, 'tcx>, | ||
) -> Result<(), Vec<FulfillmentError<'tcx>>> { | ||
self.select_where_possible(infcx)?; | ||
|
||
if self.obligations.is_empty() { | ||
Ok(()) | ||
} else { | ||
let errors = self.obligations.iter() | ||
.map(|obligation| FulfillmentError { | ||
obligation: obligation.goal.clone(), | ||
code: FulfillmentErrorCode::CodeAmbiguity, | ||
}) | ||
.collect(); | ||
Err(errors) | ||
} | ||
} | ||
|
||
fn select_where_possible( | ||
&mut self, | ||
infcx: &InferCtxt<'_, 'gcx, 'tcx>, | ||
) -> Result<(), Vec<FulfillmentError<'tcx>>> { | ||
let mut errors = Vec::new(); | ||
let mut next_round = FxHashSet::default(); | ||
let mut making_progress; | ||
|
||
loop { | ||
making_progress = false; | ||
|
||
// We iterate over all obligations, and record if we are able | ||
// to unambiguously prove at least one obligation. | ||
for obligation in self.obligations.drain() { | ||
let mut orig_values = OriginalQueryValues::default(); | ||
let canonical_goal = infcx.canonicalize_query(&InEnvironment { | ||
environment: obligation.environment, | ||
goal: obligation.goal.predicate, | ||
}, &mut orig_values); | ||
|
||
match infcx.tcx.global_tcx().evaluate_goal(canonical_goal) { | ||
Ok(response) => { | ||
if response.is_proven() { | ||
making_progress = true; | ||
|
||
match infcx.instantiate_query_response_and_region_obligations( | ||
&obligation.goal.cause, | ||
obligation.goal.param_env, | ||
&orig_values, | ||
&response | ||
) { | ||
Ok(infer_ok) => next_round.extend( | ||
infer_ok.obligations | ||
.into_iter() | ||
.map(|obligation| in_environment(infcx, obligation)) | ||
), | ||
|
||
Err(_err) => errors.push(FulfillmentError { | ||
obligation: obligation.goal, | ||
code: FulfillmentErrorCode::CodeSelectionError( | ||
SelectionError::Unimplemented | ||
), | ||
}), | ||
} | ||
} else { | ||
// Ambiguous: retry at next round. | ||
next_round.insert(obligation); | ||
} | ||
} | ||
|
||
Err(NoSolution) => errors.push(FulfillmentError { | ||
obligation: obligation.goal, | ||
code: FulfillmentErrorCode::CodeSelectionError( | ||
SelectionError::Unimplemented | ||
), | ||
}) | ||
} | ||
} | ||
next_round = std::mem::replace(&mut self.obligations, next_round); | ||
|
||
if !making_progress { | ||
break; | ||
} | ||
} | ||
|
||
if errors.is_empty() { | ||
Ok(()) | ||
} else { | ||
Err(errors) | ||
} | ||
} | ||
|
||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { | ||
self.obligations.iter().map(|obligation| obligation.goal.clone()).collect() | ||
} | ||
} |
Oops, something went wrong.