Skip to content

Commit

Permalink
Auto merge of rust-lang#113293 - GuillaumeGomez:rollup-2395uw0, r=Gui…
Browse files Browse the repository at this point in the history
…llaumeGomez

Rollup of 3 pull requests

Successful merges:

 - rust-lang#112869 (Implement selection via new trait solver)
 - rust-lang#113285 ([rustdoc] Fix display of long inline cfg labels)
 - rust-lang#113286 (Don't perform selection if inherent associated types are not enabled)

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jul 3, 2023
2 parents 3ae0ef7 + 157bab6 commit 8931edf
Show file tree
Hide file tree
Showing 19 changed files with 551 additions and 127 deletions.
14 changes: 9 additions & 5 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Expand Up @@ -1893,6 +1893,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
let tcx = self.tcx();

// Don't attempt to look up inherent associated types when the feature is not enabled.
// Theoretically it'd be fine to do so since we feature-gate their definition site.
// However, due to current limitations of the implementation (caused by us performing
// selection in AstConv), IATs can lead to cycle errors (#108491, #110106) which mask the
// feature-gate error, needlessly confusing users that use IATs by accident (#113265).
if !tcx.features().inherent_associated_types {
return Ok(None);
}

let candidates: Vec<_> = tcx
.inherent_impls(adt_did)
.iter()
Expand All @@ -1903,11 +1912,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return Ok(None);
}

if !tcx.features().inherent_associated_types {
tcx.sess
.delay_span_bug(span, "found inherent assoc type without the feature being gated");
}

//
// Select applicable inherent associated type candidates modulo regions.
//
Expand Down
51 changes: 36 additions & 15 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Expand Up @@ -49,7 +49,7 @@ pub(super) enum CandidateSource {
/// 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,
BuiltinImpl(BuiltinImplSource),
/// An assumption from the environment.
///
/// More precisely we've used the `n-th` assumption in the `param_env`.
Expand Down Expand Up @@ -87,6 +87,16 @@ pub(super) enum CandidateSource {
AliasBound,
}

/// Records additional information about what kind of built-in impl this is.
/// This should only be used by selection.
#[derive(Debug, Clone, Copy)]
pub(super) enum BuiltinImplSource {
TraitUpcasting,
Object,
Misc,
Ambiguity,
}

/// 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 @@ -295,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// least structurally resolve the type one layer.
if goal.predicate.self_ty().is_ty_var() {
return vec![Candidate {
source: CandidateSource::BuiltinImpl,
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
result: self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
.unwrap(),
Expand Down Expand Up @@ -344,7 +354,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let result = ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::Maybe(MaybeCause::Overflow),
)?;
Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }])
Ok(vec![Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
result,
}])
},
|ecx| {
let normalized_ty = ecx.next_ty_infer();
Expand Down Expand Up @@ -447,17 +460,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};

match result {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
}
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => (),
}

// There may be multiple unsize candidates for a trait with several supertraits:
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) {
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result });
candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
result,
});
}
}
}
Expand Down Expand Up @@ -621,9 +638,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
};

match result {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
}
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => (),
}
}
Expand Down Expand Up @@ -688,9 +706,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}

match G::consider_object_bound_candidate(self, goal, assumption) {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
}
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object),
result,
}),
Err(NoSolution) => (),
}
}
Expand All @@ -711,8 +730,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(_) => match self
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
{
Ok(result) => candidates
.push(Candidate { source: CandidateSource::BuiltinImpl, result }),
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity),
result,
}),
// FIXME: This will be reachable at some point if we're in
// `assemble_candidates_after_normalizing_self_ty` and we get a
// universe error. We'll deal with it at this point.
Expand Down
152 changes: 96 additions & 56 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
Expand Up @@ -28,9 +28,11 @@ use super::inspect::ProofTreeBuilder;
use super::search_graph::{self, OverflowHandler};
use super::SolverMode;
use super::{search_graph::SearchGraph, Goal};
pub use select::InferCtxtSelectExt;

mod canonical;
mod probe;
mod select;

pub struct EvalCtxt<'a, 'tcx> {
/// The inference context that backs (mostly) inference and placeholder terms
Expand Down Expand Up @@ -140,28 +142,47 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
Option<inspect::GoalEvaluation<'tcx>>,
) {
let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode);
EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
ecx.evaluate_goal(IsNormalizesToHack::No, goal)
})
}
}

impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
pub(super) fn solver_mode(&self) -> SolverMode {
self.search_graph.solver_mode()
}

/// Creates a root evaluation context and search graph. This should only be
/// used from outside of any evaluation, and other methods should be preferred
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
fn enter_root<R>(
infcx: &InferCtxt<'tcx>,
generate_proof_tree: GenerateProofTree,
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);

let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
infcx: self,
infcx: infcx,
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
predefined_opaques_in_body: self
predefined_opaques_in_body: infcx
.tcx
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
// Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
nested_goals: NestedGoals::new(),
tainted: Ok(()),
inspect: (self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
inspect: (infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree
|| matches!(generate_proof_tree, GenerateProofTree::Yes))
.then(ProofTreeBuilder::new_root)
.unwrap_or_else(ProofTreeBuilder::new_noop),
};
let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal);
let result = f(&mut ecx);

let tree = ecx.inspect.finalize();
if let Some(tree) = &tree {
Expand All @@ -177,11 +198,66 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
assert!(search_graph.is_empty());
(result, tree)
}
}

impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
pub(super) fn solver_mode(&self) -> SolverMode {
self.search_graph.solver_mode()
/// Creates a nested evaluation context that shares the same search graph as the
/// one passed in. This is suitable for evaluation, granted that the search graph
/// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]),
/// but it's preferable to use other methods that call this one rather than this
/// method directly.
///
/// This function takes care of setting up the inference context, setting the anchor,
/// and registering opaques from the canonicalized input.
fn enter_canonical<R>(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_input: CanonicalInput<'tcx>,
goal_evaluation: &mut ProofTreeBuilder<'tcx>,
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
) -> R {
let intercrate = match search_graph.solver_mode() {
SolverMode::Normal => false,
SolverMode::Coherence => true,
};
let (ref infcx, input, var_values) = tcx
.infer_ctxt()
.intercrate(intercrate)
.with_next_trait_solver(true)
.with_opaque_type_inference(canonical_input.value.anchor)
.build_with_canonical(DUMMY_SP, &canonical_input);

let mut ecx = EvalCtxt {
infcx,
var_values,
predefined_opaques_in_body: input.predefined_opaques_in_body,
max_input_universe: canonical_input.max_universe,
search_graph,
nested_goals: NestedGoals::new(),
tainted: Ok(()),
inspect: goal_evaluation.new_goal_evaluation_step(input),
};

for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
ecx.insert_hidden_type(key, input.goal.param_env, ty)
.expect("failed to prepopulate opaque types");
}

if !ecx.nested_goals.is_empty() {
panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
}

let result = f(&mut ecx, input.goal);

goal_evaluation.goal_evaluation_step(ecx.inspect);

// When creating a query response we clone the opaque type constraints
// instead of taking them. This would cause an ICE here, since we have
// assertions against dropping an `InferCtxt` without taking opaques.
// FIXME: Once we remove support for the old impl we can remove this.
if input.anchor != DefiningAnchor::Error {
let _ = infcx.take_opaque_types();
}

result
}

/// The entry point of the solver.
Expand Down Expand Up @@ -210,53 +286,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
canonical_input,
goal_evaluation,
|search_graph, goal_evaluation| {
let intercrate = match search_graph.solver_mode() {
SolverMode::Normal => false,
SolverMode::Coherence => true,
};
let (ref infcx, input, var_values) = tcx
.infer_ctxt()
.intercrate(intercrate)
.with_next_trait_solver(true)
.with_opaque_type_inference(canonical_input.value.anchor)
.build_with_canonical(DUMMY_SP, &canonical_input);

let mut ecx = EvalCtxt {
infcx,
var_values,
predefined_opaques_in_body: input.predefined_opaques_in_body,
max_input_universe: canonical_input.max_universe,
EvalCtxt::enter_canonical(
tcx,
search_graph,
nested_goals: NestedGoals::new(),
tainted: Ok(()),
inspect: goal_evaluation.new_goal_evaluation_step(input),
};

for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
ecx.insert_hidden_type(key, input.goal.param_env, ty)
.expect("failed to prepopulate opaque types");
}

if !ecx.nested_goals.is_empty() {
panic!(
"prepopulating opaque types shouldn't add goals: {:?}",
ecx.nested_goals
);
}

let result = ecx.compute_goal(input.goal);
ecx.inspect.query_result(result);
goal_evaluation.goal_evaluation_step(ecx.inspect);

// When creating a query response we clone the opaque type constraints
// instead of taking them. This would cause an ICE here, since we have
// assertions against dropping an `InferCtxt` without taking opaques.
// FIXME: Once we remove support for the old impl we can remove this.
if input.anchor != DefiningAnchor::Error {
let _ = infcx.take_opaque_types();
}

result
canonical_input,
goal_evaluation,
|ecx, goal| {
let result = ecx.compute_goal(goal);
ecx.inspect.query_result(result);
result
},
)
},
)
}
Expand Down
Expand Up @@ -20,18 +20,18 @@ use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
};
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;

impl<'tcx> EvalCtxt<'_, 'tcx> {
/// Canonicalizes the goal remembering the original values
/// for each bound variable.
pub(super) fn canonicalize_goal(
pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
goal: Goal<'tcx, T>,
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize(
self.infcx,
Expand Down

0 comments on commit 8931edf

Please sign in to comment.