Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1f34e11
Lift `T: Sized` bounds from some `strict_provenance` pointer methods
WaffleLapkin Oct 28, 2022
662f1f2
Lift `T: Sized` bounds from some `strict_provenance` `NonNull` methods
WaffleLapkin Nov 7, 2022
ee59533
relax reference requirement on from_abstract_name
mllken Jan 4, 2023
47014b1
Don't do pointer arithmetic on pointers to deallocated memory
the8472 Jan 16, 2023
2d54b7c
add miri regression test
the8472 Jan 17, 2023
6d7e213
rustdoc: remove function `handleClick` that's only used once
notriddle Jan 18, 2023
708b529
rustdoc: stop using deprecated `window.event` when there's an `ev` param
notriddle Jan 18, 2023
b738b06
update cache
lcnr Jan 11, 2023
bf7dbff
instantiate canonical vars eagerly
lcnr Jan 17, 2023
660c283
remove assembly context and impl a bit more
lcnr Jan 17, 2023
9a757d6
add `eq` to `InferCtxtExt`
lcnr Jan 17, 2023
31ac29d
update project to emulate a projection cache
lcnr Jan 17, 2023
415aa66
add note about indirect cycles
lcnr Jan 17, 2023
369f9aa
add comment
lcnr Jan 18, 2023
d6ea99d
Rollup merge of #103702 - WaffleLapkin:lift-sized-bounds-from-pointer…
Dylan-DPC Jan 18, 2023
548ae60
Rollup merge of #106441 - mllken:abstract-socket-noref, r=joshtriplett
Dylan-DPC Jan 18, 2023
6f5c3c9
Rollup merge of #106718 - lcnr:solver-cycles, r=compiler-errors
Dylan-DPC Jan 18, 2023
1ff4a12
Rollup merge of #106950 - the8472:fix-splice-miri, r=cuviper
Dylan-DPC Jan 18, 2023
43b1e73
Rollup merge of #107014 - notriddle:notriddle/js-cleanup, r=Guillaume…
Dylan-DPC Jan 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
remove assembly context and impl a bit more
  • Loading branch information
lcnr committed Jan 18, 2023
commit 660c28391c79bd12e116724a8877a2148630dee5
6 changes: 6 additions & 0 deletions compiler/rustc_middle/src/infer/canonical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,12 @@ TrivialTypeTraversalAndLiftImpls! {
}

impl<'tcx> CanonicalVarValues<'tcx> {
/// Creates dummy var values which should not be used in a
/// canonical response.
pub fn dummy() -> CanonicalVarValues<'tcx> {
CanonicalVarValues { var_values: Default::default() }
}

#[inline]
pub fn len(&self) -> usize {
self.var_values.len()
Expand Down
254 changes: 206 additions & 48 deletions compiler/rustc_trait_selection/src/solve/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,112 +12,270 @@ use std::fmt::Debug;
///
/// It consists of both the `source`, which describes how that goal would be proven,
/// and the `result` when using the given `source`.
///
/// For the list of possible candidates, please look at the documentation of
/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource].
#[derive(Debug, Clone)]
pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> {
pub(super) source: G::CandidateSource,
pub(super) struct Candidate<'tcx> {
pub(super) source: CandidateSource,
pub(super) result: CanonicalResponse<'tcx>,
}

pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
type CandidateSource: Debug + Copy;
/// Possible ways the given goal can be proven.
#[derive(Debug, Clone, Copy)]
pub(super) enum CandidateSource {
/// A user written impl.
///
/// ## Examples
///
/// ```rust
/// fn main() {
/// let x: Vec<u32> = Vec::new();
/// // This uses the impl from the standard library to prove `Vec<T>: Clone`.
/// let y = x.clone();
/// }
/// ```
Impl(DefId),
/// A builtin impl generated by the compiler. When adding a new special
/// trait, try to use actual impls whenever possible. Builtin impls should
/// only be used in cases where the impl cannot be manually be written.
///
/// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
/// For a list of all traits with builtin impls, check out the
/// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
BuiltinImpl,
/// An assumption from the environment.
///
/// More precicely we've used the `n-th` assumption in the `param_env`.
///
/// ## Examples
///
/// ```rust
/// fn is_clone<T: Clone>(x: T) -> (T, T) {
/// // This uses the assumption `T: Clone` from the `where`-bounds
/// // to prove `T: Clone`.
/// (x.clone(), x)
/// }
/// ```
ParamEnv(usize),
/// If the self type is an alias type, e.g. an opaque type or a projection,
/// we know the bounds on that alias to hold even without knowing its concrete
/// underlying type.
///
/// More precisely this candidate is using the `n-th` bound in the `item_bounds` of
/// the self type.
///
/// ## Examples
///
/// ```rust
/// trait Trait {
/// type Assoc: Clone;
/// }
///
/// fn foo<T: Trait>(x: <T as Trait>::Assoc) {
/// // We prove `<T as Trait>::Assoc` by looking at the bounds on `Assoc` in
/// // in the trait definition.
/// let _y = x.clone();
/// }
/// ```
AliasBound(usize),
}

pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
fn self_ty(self) -> Ty<'tcx>;

fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;

fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;

fn consider_impl_candidate(
acx: &mut AssemblyCtxt<'_, '_, 'tcx, Self>,
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
impl_def_id: DefId,
);
}
) -> Result<Certainty, NoSolution>;

/// An abstraction which correctly deals with the canonical results for candidates.
///
/// It also deduplicates the behavior between trait and projection predicates.
pub(super) struct AssemblyCtxt<'a, 'b, 'tcx, G: GoalKind<'tcx>> {
pub(super) cx: &'a mut EvalCtxt<'b, 'tcx>,
candidates: Vec<Candidate<'tcx, G>>,
}
fn consider_builtin_sized_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Result<Certainty, NoSolution>;

impl<'a, 'b, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'b, 'tcx, G> {
pub(super) fn assemble_and_evaluate_candidates(
cx: &'a mut EvalCtxt<'b, 'tcx>,
fn consider_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
assumption: ty::Predicate<'tcx>,
) -> Result<Certainty, NoSolution>;
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
) -> Vec<Candidate<'tcx, G>> {
let mut acx = AssemblyCtxt { cx, candidates: Vec::new() };
) -> Vec<Candidate<'tcx>> {
let mut candidates = Vec::new();

acx.assemble_candidates_after_normalizing_self_ty(goal);
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);

acx.assemble_impl_candidates(goal);
self.assemble_impl_candidates(goal, &mut candidates);

acx.candidates
}
self.assemble_builtin_impl_candidates(goal, &mut candidates);

pub(super) fn try_insert_candidate(
&mut self,
source: G::CandidateSource,
certainty: Certainty,
) {
match self.cx.make_canonical_response(certainty) {
Ok(result) => self.candidates.push(Candidate { source, result }),
Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
}
self.assemble_param_env_candidates(goal, &mut candidates);

self.assemble_alias_bound_candidates(goal, &mut candidates);

candidates
}

/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
///
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
/// this case as projections as self types add `
fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
let tcx = self.cx.tcx();
let infcx = self.cx.infcx;
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
return
};
infcx.probe(|_| {
let normalized_ty = infcx.next_ty_infer();
self.infcx.probe(|_| {
let normalized_ty = self.infcx.next_ty_infer();
let normalizes_to_goal = goal.with(
tcx,
ty::Binder::dummy(ty::ProjectionPredicate {
projection_ty,
term: normalized_ty.into(),
}),
);
let normalization_certainty = match self.cx.evaluate_goal(normalizes_to_goal) {
let normalization_certainty = match self.evaluate_goal(normalizes_to_goal) {
Ok((_, certainty)) => certainty,
Err(NoSolution) => return,
};

// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
// This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
// This doesn't work as long as we use `CandidateSource` in winnowing.
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
let normalized_candidates =
AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
// FIXME: This is broken if we care about the `usize` of `AliasBound` because the self type
// could be normalized to yet another projection with different item bounds.
let normalized_candidates = self.assemble_and_evaluate_candidates(goal);
for mut normalized_candidate in normalized_candidates {
normalized_candidate.result =
normalized_candidate.result.unchecked_map(|mut response| {
// FIXME: This currently hides overflow in the normalization step of the self type
// which is probably wrong. Maybe `unify_and` should actually keep overflow as
// we treat it as non-fatal anyways.
response.certainty = response.certainty.unify_and(normalization_certainty);
response
});
self.candidates.push(normalized_candidate);
candidates.push(normalized_candidate);
}
})
}

fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
let tcx = self.cx.tcx();
fn assemble_impl_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
tcx.for_each_relevant_impl(
goal.predicate.trait_def_id(tcx),
goal.predicate.self_ty(),
|impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
|impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id)
.and_then(|certainty| self.make_canonical_response(certainty))
{
Ok(result) => candidates
.push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
Err(NoSolution) => (),
},
);
}

fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
let lang_items = self.tcx().lang_items();
let trait_def_id = goal.predicate.trait_def_id(self.tcx());
let result = if lang_items.sized_trait() == Some(trait_def_id) {
G::consider_builtin_sized_candidate(self, goal)
} else {
Err(NoSolution)
};

match result.and_then(|certainty| self.make_canonical_response(certainty)) {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
}
Err(NoSolution) => (),
}
}

fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
match G::consider_assumption(self, goal, assumption)
.and_then(|certainty| self.make_canonical_response(certainty))
{
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
}
Err(NoSolution) => (),
}
}
}

fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
&mut self,
goal: Goal<'tcx, G>,
candidates: &mut Vec<Candidate<'tcx>>,
) {
let alias_ty = match goal.predicate.self_ty().kind() {
ty::Bool
| ty::Char
| ty::Int(_)
| ty::Uint(_)
| ty::Float(_)
| ty::Adt(_, _)
| ty::Foreign(_)
| ty::Str
| ty::Array(_, _)
| ty::Slice(_)
| ty::RawPtr(_)
| ty::Ref(_, _, _)
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(_)
| ty::Never
| ty::Tuple(_)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Error(_) => return,
ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
ty::Alias(_, alias_ty) => alias_ty,
};

for (i, (assumption, _)) in self
.tcx()
.bound_explicit_item_bounds(alias_ty.def_id)
.subst_iter_copied(self.tcx(), alias_ty.substs)
.enumerate()
{
match G::consider_assumption(self, goal, assumption)
.and_then(|certainty| self.make_canonical_response(certainty))
{
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::AliasBound(i), result })
}
Err(NoSolution) => (),
}
}
}
}
27 changes: 6 additions & 21 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ use std::mem;

use rustc_data_structures::fx::FxHashMap;
use rustc_infer::{
infer::{canonical::OriginalQueryValues, InferCtxt},
infer::InferCtxt,
traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
SelectionError, TraitEngine,
},
};
use rustc_middle::ty;

use super::{Certainty, EvalCtxt};
use super::{search_graph, Certainty, EvalCtxt};

/// A trait engine using the new trait solver.
///
Expand Down Expand Up @@ -68,25 +68,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
let mut has_changed = false;
for obligation in mem::take(&mut self.obligations) {
let goal = obligation.clone().into();

// FIXME: Add a better API for that '^^
let mut orig_values = OriginalQueryValues::default();
let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
let (changed, certainty) = match EvalCtxt::evaluate_canonical_goal(
infcx.tcx,
&mut super::search_graph::SearchGraph::new(infcx.tcx),
canonical_goal,
) {
Ok(canonical_response) => {
(
true, // FIXME: check whether `var_values` are an identity substitution.
super::instantiate_canonical_query_response(
infcx,
&orig_values,
canonical_response,
),
)
}
let search_graph = &mut search_graph::SearchGraph::new(infcx.tcx);
let mut ecx = EvalCtxt::new_outside_solver(infcx, search_graph);
let (changed, certainty) = match ecx.evaluate_goal(goal) {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
Expand Down
Loading