Skip to content

Commit

Permalink
inspect: strongly typed CandidateKind
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Sep 11, 2023
1 parent 006d599 commit dba2d6c
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 121 deletions.
63 changes: 63 additions & 0 deletions compiler/rustc_middle/src/traits/solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ use crate::ty::{
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
TypeVisitor,
};
use rustc_span::def_id::DefId;

use super::BuiltinImplSource;

mod cache;
pub mod inspect;
Expand Down Expand Up @@ -235,3 +238,63 @@ pub enum IsNormalizesToHack {
Yes,
No,
}

/// Possible ways the given goal can be proven.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub 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.
BuiltinImpl(BuiltinImplSource),
/// An assumption from the environment.
///
/// More precisely 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,
}
33 changes: 19 additions & 14 deletions compiler/rustc_middle/src/traits/solve/inspect.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
use super::{
CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput, QueryResult,
CandidateSource, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution, QueryInput,
QueryResult,
};
use crate::ty;
use format::ProofTreeFormatter;
use std::fmt::{Debug, Write};

mod format;

#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
#[derive(Debug, Eq, PartialEq)]
pub enum CacheHit {
Provisional,
Global,
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub struct GoalEvaluation<'tcx> {
pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
pub is_normalizes_to_hack: IsNormalizesToHack,
pub evaluation: CanonicalGoalEvaluation<'tcx>,
pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub struct CanonicalGoalEvaluation<'tcx> {
pub goal: CanonicalInput<'tcx>,
pub kind: GoalEvaluationKind<'tcx>,
pub result: QueryResult<'tcx>,
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub enum GoalEvaluationKind<'tcx> {
CacheHit(CacheHit),
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
Expand All @@ -39,13 +40,13 @@ impl Debug for GoalEvaluation<'_> {
}
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub struct AddedGoalsEvaluation<'tcx> {
pub evaluations: Vec<Vec<GoalEvaluation<'tcx>>>,
pub result: Result<Certainty, NoSolution>,
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub struct GoalEvaluationStep<'tcx> {
pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,

Expand All @@ -55,24 +56,28 @@ pub struct GoalEvaluationStep<'tcx> {
pub result: QueryResult<'tcx>,
}

#[derive(Eq, PartialEq, Hash, HashStable)]
#[derive(Eq, PartialEq)]
pub struct GoalCandidate<'tcx> {
pub added_goals_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
pub candidates: Vec<GoalCandidate<'tcx>>,
pub kind: CandidateKind<'tcx>,
pub kind: ProbeKind<'tcx>,
}

#[derive(Eq, PartialEq, Debug, Hash, HashStable)]
pub enum CandidateKind<'tcx> {
#[derive(Debug, PartialEq, Eq)]
pub enum ProbeKind<'tcx> {
/// Probe entered when normalizing the self ty during candidate assembly
NormalizedSelfTyAssembly,
/// A normal candidate for proving a goal
Candidate { name: String, result: QueryResult<'tcx> },
/// Some candidate to prove the current goal.
///
/// FIXME: Remove this in favor of always using more strongly typed variants.
MiscCandidate { name: &'static str, result: QueryResult<'tcx> },
/// A candidate for proving a trait or alias-relate goal.
TraitCandidate { source: CandidateSource, result: QueryResult<'tcx> },
/// Used in the probe that wraps normalizing the non-self type for the unsize
/// trait, which is also structurally matched on.
UnsizeAssembly,
/// 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
/// the source type upholds all of the target type's object bounds.
UpcastProbe,
UpcastProjectionCompatibility,
}
11 changes: 7 additions & 4 deletions compiler/rustc_middle/src/traits/solve/inspect/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,21 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {

pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
match &candidate.kind {
CandidateKind::NormalizedSelfTyAssembly => {
ProbeKind::NormalizedSelfTyAssembly => {
writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:")
}
CandidateKind::UnsizeAssembly => {
ProbeKind::UnsizeAssembly => {
writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:")
}
CandidateKind::UpcastProbe => {
ProbeKind::UpcastProjectionCompatibility => {
writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:")
}
CandidateKind::Candidate { name, result } => {
ProbeKind::MiscCandidate { name, result } => {
writeln!(self.f, "CANDIDATE {name}: {result:?}")
}
ProbeKind::TraitCandidate { source, result } => {
writeln!(self.f, "CANDIDATE {source:?}: {result:?}")
}
}?;

self.nested(|this| {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_trait_selection/src/solve/alias_relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
direction: ty::AliasRelationDirection,
invert: Invert,
) -> QueryResult<'tcx> {
self.probe_candidate("normalizes-to").enter(|ecx| {
self.probe_misc_candidate("normalizes-to").enter(|ecx| {
ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
Expand Down Expand Up @@ -175,7 +175,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
alias_rhs: ty::AliasTy<'tcx>,
direction: ty::AliasRelationDirection,
) -> QueryResult<'tcx> {
self.probe_candidate("args relate").enter(|ecx| {
self.probe_misc_candidate("args relate").enter(|ecx| {
match direction {
ty::AliasRelationDirection::Equate => {
ecx.eq(param_env, alias_lhs, alias_rhs)?;
Expand All @@ -196,7 +196,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
rhs: ty::Term<'tcx>,
direction: ty::AliasRelationDirection,
) -> QueryResult<'tcx> {
self.probe_candidate("bidir normalizes-to").enter(|ecx| {
self.probe_misc_candidate("bidir normalizes-to").enter(|ecx| {
ecx.normalizes_to_inner(
param_env,
lhs.to_alias_ty(ecx.tcx()).unwrap(),
Expand Down
70 changes: 6 additions & 64 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ use crate::traits::coherence;
use rustc_hir::def_id::DefId;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Reveal;
use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
use rustc_middle::traits::solve::inspect::ProbeKind;
use rustc_middle::traits::solve::{
CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
};
use rustc_middle::traits::BuiltinImplSource;
use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
use rustc_middle::ty::{self, Ty, TyCtxt};
Expand All @@ -27,66 +29,6 @@ pub(super) struct Candidate<'tcx> {
pub(super) result: CanonicalResponse<'tcx>,
}

/// 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(BuiltinImplSource),
/// An assumption from the environment.
///
/// More precisely 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,
}

/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>:
TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
Expand Down Expand Up @@ -399,7 +341,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let tcx = self.tcx();
let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return };

candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| {
candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
if num_steps < ecx.local_overflow_limit() {
let normalized_ty = ecx.next_ty_infer();
let normalizes_to_goal = goal.with(
Expand Down Expand Up @@ -910,7 +852,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
SolverMode::Coherence => {}
};

let result = self.probe_candidate("coherence unknowable").enter(|ecx| {
let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
let trait_ref = goal.predicate.trait_ref(tcx);

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if candidate_key.def_id != key.def_id {
continue;
}
values.extend(self.probe_candidate("opaque type storage").enter(|ecx| {
values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| {
for (a, b) in std::iter::zip(candidate_key.args, key.args) {
ecx.eq(param_env, a, b)?;
}
Expand Down

0 comments on commit dba2d6c

Please sign in to comment.