Skip to content
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

Rollup of 3 pull requests #113293

Merged
merged 8 commits into from
Jul 3, 2023
14 changes: 9 additions & 5 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading