Auto merge of #117513 - matthiaskrgr:rollup-jvl6y84, r=matthiaskrgr
Rollup of 4 pull requests Successful merges: - #117394 (use global cache when computing proof trees) - #117495 (Clarify `Unsize` documentation) - #117509 (Remove support for alias `-Z symbol-mangling-version`) - #117512 (Expand mem::offset_of! docs) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
c5afe0a61e
@ -817,7 +817,6 @@ fn test_unstable_options_tracking_hash() {
|
||||
tracked!(split_lto_unit, Some(true));
|
||||
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
|
||||
tracked!(stack_protector, StackProtector::All);
|
||||
tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
|
||||
tracked!(teach, true);
|
||||
tracked!(thinlto, Some(true));
|
||||
tracked!(thir_unsafeck, true);
|
||||
|
@ -69,6 +69,7 @@ macro_rules! arena_types {
|
||||
[] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
|
||||
[] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
|
||||
[] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
|
||||
[] canonical_goal_evaluation: rustc_middle::traits::solve::inspect::GoalEvaluationStep<'tcx>,
|
||||
[] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
|
||||
[] type_op_subtype:
|
||||
rustc_middle::infer::canonical::Canonical<'tcx,
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{CanonicalInput, QueryResult};
|
||||
use super::{inspect, CanonicalInput, QueryResult};
|
||||
use crate::ty::TyCtxt;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lock;
|
||||
@ -14,8 +14,10 @@ pub struct EvaluationCache<'tcx> {
|
||||
map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub struct CacheData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
pub reached_depth: usize,
|
||||
pub encountered_overflow: bool,
|
||||
}
|
||||
@ -24,22 +26,33 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
/// Insert a final result into the global cache.
|
||||
pub fn insert(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
key: CanonicalInput<'tcx>,
|
||||
proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
reached_depth: usize,
|
||||
did_overflow: bool,
|
||||
encountered_overflow: bool,
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
dep_node: DepNodeIndex,
|
||||
result: QueryResult<'tcx>,
|
||||
) {
|
||||
let mut map = self.map.borrow_mut();
|
||||
let entry = map.entry(key).or_default();
|
||||
let data = WithDepNode::new(dep_node, result);
|
||||
let data = WithDepNode::new(dep_node, QueryData { result, proof_tree });
|
||||
entry.cycle_participants.extend(cycle_participants);
|
||||
if did_overflow {
|
||||
if encountered_overflow {
|
||||
entry.with_overflow.insert(reached_depth, data);
|
||||
} else {
|
||||
entry.success = Some(Success { data, reached_depth });
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
drop(map);
|
||||
if Some(CacheData { result, proof_tree, reached_depth, encountered_overflow })
|
||||
!= self.get(tcx, key, |_| false, Limit(reached_depth))
|
||||
{
|
||||
bug!("unable to retrieve inserted element from cache: {key:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to fetch a cached result, checking the recursion limit
|
||||
@ -62,27 +75,39 @@ impl<'tcx> EvaluationCache<'tcx> {
|
||||
|
||||
if let Some(ref success) = entry.success {
|
||||
if available_depth.value_within_limit(success.reached_depth) {
|
||||
let QueryData { result, proof_tree } = success.data.get(tcx);
|
||||
return Some(CacheData {
|
||||
result: success.data.get(tcx),
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: success.reached_depth,
|
||||
encountered_overflow: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entry.with_overflow.get(&available_depth.0).map(|e| CacheData {
|
||||
result: e.get(tcx),
|
||||
reached_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
entry.with_overflow.get(&available_depth.0).map(|e| {
|
||||
let QueryData { result, proof_tree } = e.get(tcx);
|
||||
CacheData {
|
||||
result,
|
||||
proof_tree,
|
||||
reached_depth: available_depth.0,
|
||||
encountered_overflow: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Success<'tcx> {
|
||||
data: WithDepNode<QueryResult<'tcx>>,
|
||||
data: WithDepNode<QueryData<'tcx>>,
|
||||
reached_depth: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct QueryData<'tcx> {
|
||||
pub result: QueryResult<'tcx>,
|
||||
pub proof_tree: Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]>,
|
||||
}
|
||||
|
||||
/// The cache entry for a goal `CanonicalInput`.
|
||||
///
|
||||
/// This contains results whose computation never hit the
|
||||
@ -96,5 +121,5 @@ struct CacheEntry<'tcx> {
|
||||
/// See the doc comment of `StackEntry::cycle_participants` for more
|
||||
/// details.
|
||||
cycle_participants: FxHashSet<CanonicalInput<'tcx>>,
|
||||
with_overflow: FxHashMap<usize, WithDepNode<QueryResult<'tcx>>>,
|
||||
with_overflow: FxHashMap<usize, WithDepNode<QueryData<'tcx>>>,
|
||||
}
|
||||
|
@ -42,12 +42,6 @@ pub struct State<'tcx, T> {
|
||||
|
||||
pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum CacheHit {
|
||||
Provisional,
|
||||
Global,
|
||||
}
|
||||
|
||||
/// When evaluating the root goals we also store the
|
||||
/// original values for the `CanonicalVarValues` of the
|
||||
/// canonicalized goal. We use this to map any [CanonicalState]
|
||||
@ -78,8 +72,8 @@ pub struct CanonicalGoalEvaluation<'tcx> {
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum CanonicalGoalEvaluationKind<'tcx> {
|
||||
Overflow,
|
||||
CacheHit(CacheHit),
|
||||
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
|
||||
CycleInStack,
|
||||
Evaluation { revisions: &'tcx [GoalEvaluationStep<'tcx>] },
|
||||
}
|
||||
impl Debug for GoalEvaluation<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
@ -74,13 +74,10 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
CanonicalGoalEvaluationKind::Overflow => {
|
||||
writeln!(self.f, "OVERFLOW: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
|
||||
writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
|
||||
CanonicalGoalEvaluationKind::CycleInStack => {
|
||||
writeln!(self.f, "CYCLE IN STACK: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
|
||||
writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
|
||||
}
|
||||
CanonicalGoalEvaluationKind::Uncached { revisions } => {
|
||||
CanonicalGoalEvaluationKind::Evaluation { revisions } => {
|
||||
for (n, step) in revisions.iter().enumerate() {
|
||||
writeln!(self.f, "REVISION {n}")?;
|
||||
self.nested(|this| this.format_evaluation_step(step))?;
|
||||
|
@ -2674,28 +2674,19 @@ pub fn build_session_options(
|
||||
);
|
||||
}
|
||||
|
||||
// Handle both `-Z symbol-mangling-version` and `-C symbol-mangling-version`; the latter takes
|
||||
// precedence.
|
||||
match (cg.symbol_mangling_version, unstable_opts.symbol_mangling_version) {
|
||||
(Some(smv_c), Some(smv_z)) if smv_c != smv_z => {
|
||||
handler.early_error(
|
||||
"incompatible values passed for `-C symbol-mangling-version` \
|
||||
and `-Z symbol-mangling-version`",
|
||||
);
|
||||
// Check for unstable values of `-C symbol-mangling-version`.
|
||||
// This is what prevents them from being used on stable compilers.
|
||||
match cg.symbol_mangling_version {
|
||||
// Stable values:
|
||||
None | Some(SymbolManglingVersion::V0) => {}
|
||||
// Unstable values:
|
||||
Some(SymbolManglingVersion::Legacy) => {
|
||||
if !unstable_opts.unstable_options {
|
||||
handler.early_error(
|
||||
"`-C symbol-mangling-version=legacy` requires `-Z unstable-options`",
|
||||
);
|
||||
}
|
||||
}
|
||||
(Some(SymbolManglingVersion::V0), _) => {}
|
||||
(Some(_), _) if !unstable_opts.unstable_options => {
|
||||
handler
|
||||
.early_error("`-C symbol-mangling-version=legacy` requires `-Z unstable-options`");
|
||||
}
|
||||
(None, None) => {}
|
||||
(None, smv) => {
|
||||
handler.early_warn(
|
||||
"`-Z symbol-mangling-version` is deprecated; use `-C symbol-mangling-version`",
|
||||
);
|
||||
cg.symbol_mangling_version = smv;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Check for unstable values of `-C instrument-coverage`.
|
||||
|
@ -1529,8 +1529,6 @@ options! {
|
||||
dump_solver_proof_tree: DumpSolverProofTree = (DumpSolverProofTree::Never, parse_dump_solver_proof_tree, [UNTRACKED],
|
||||
"dump a proof tree for every goal evaluated by the new trait solver. If the flag is specified without any options after it
|
||||
then it defaults to `always`. If the flag is not specified at all it defaults to `on-request`."),
|
||||
dump_solver_proof_tree_use_cache: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
|
||||
"determines whether dumped proof trees use the global cache"),
|
||||
dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED],
|
||||
"version of DWARF debug information to emit (default: 2 or 4, depending on platform)"),
|
||||
dylib_lto: bool = (false, parse_bool, [UNTRACKED],
|
||||
@ -1823,9 +1821,6 @@ written to standard error output)"),
|
||||
"control if mem::uninitialized and mem::zeroed panic on more UB"),
|
||||
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
|
||||
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
|
||||
symbol_mangling_version: Option<SymbolManglingVersion> = (None,
|
||||
parse_symbol_mangling_version, [TRACKED],
|
||||
"which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
|
||||
#[rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field")]
|
||||
teach: bool = (false, parse_bool, [TRACKED],
|
||||
"show extended diagnostic help (default: no)"),
|
||||
|
@ -119,25 +119,11 @@ impl NestedGoals<'_> {
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
pub enum GenerateProofTree {
|
||||
Yes(UseGlobalCache),
|
||||
Yes,
|
||||
IfEnabled,
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
|
||||
pub enum UseGlobalCache {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
impl UseGlobalCache {
|
||||
pub fn from_bool(use_cache: bool) -> Self {
|
||||
match use_cache {
|
||||
true => UseGlobalCache::Yes,
|
||||
false => UseGlobalCache::No,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InferCtxtEvalExt<'tcx> {
|
||||
/// Evaluates a goal from **outside** of the trait solver.
|
||||
///
|
||||
|
@ -17,7 +17,7 @@ use rustc_middle::traits::solve::{Certainty, Goal};
|
||||
use rustc_middle::ty;
|
||||
|
||||
use crate::solve::inspect::ProofTreeBuilder;
|
||||
use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
|
||||
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
|
||||
|
||||
pub struct InspectGoal<'a, 'tcx> {
|
||||
infcx: &'a InferCtxt<'tcx>,
|
||||
@ -82,8 +82,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
|
||||
}
|
||||
|
||||
for &goal in &instantiated_goals {
|
||||
let (_, proof_tree) =
|
||||
infcx.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
|
||||
let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
|
||||
let proof_tree = proof_tree.unwrap();
|
||||
visitor.visit_goal(&InspectGoal::new(
|
||||
infcx,
|
||||
@ -169,11 +168,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
||||
let mut candidates = vec![];
|
||||
let last_eval_step = match self.evaluation.evaluation.kind {
|
||||
inspect::CanonicalGoalEvaluationKind::Overflow
|
||||
| inspect::CanonicalGoalEvaluationKind::CacheHit(_) => {
|
||||
| inspect::CanonicalGoalEvaluationKind::CycleInStack => {
|
||||
warn!("unexpected root evaluation: {:?}", self.evaluation);
|
||||
return vec![];
|
||||
}
|
||||
inspect::CanonicalGoalEvaluationKind::Uncached { ref revisions } => {
|
||||
inspect::CanonicalGoalEvaluationKind::Evaluation { ref revisions } => {
|
||||
if let Some(last) = revisions.last() {
|
||||
last
|
||||
} else {
|
||||
@ -227,8 +226,7 @@ impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> {
|
||||
goal: Goal<'tcx, ty::Predicate<'tcx>>,
|
||||
visitor: &mut V,
|
||||
) -> ControlFlow<V::BreakTy> {
|
||||
let (_, proof_tree) =
|
||||
self.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No));
|
||||
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
|
||||
let proof_tree = proof_tree.unwrap();
|
||||
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
//! This code is *a bit* of a mess and can hopefully be
|
||||
//! mostly ignored. For a general overview of how it works,
|
||||
//! see the comment on [ProofTreeBuilder].
|
||||
use std::mem;
|
||||
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{
|
||||
CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult,
|
||||
@ -10,7 +12,6 @@ use rustc_middle::traits::solve::{
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_session::config::DumpSolverProofTree;
|
||||
|
||||
use crate::solve::eval_ctxt::UseGlobalCache;
|
||||
use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
|
||||
|
||||
/// The core data structure when building proof trees.
|
||||
@ -34,12 +35,7 @@ use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree};
|
||||
/// is called to recursively convert the whole structure to a
|
||||
/// finished proof tree.
|
||||
pub(in crate::solve) struct ProofTreeBuilder<'tcx> {
|
||||
state: Option<Box<BuilderData<'tcx>>>,
|
||||
}
|
||||
|
||||
struct BuilderData<'tcx> {
|
||||
tree: DebugSolver<'tcx>,
|
||||
use_global_cache: UseGlobalCache,
|
||||
state: Option<Box<DebugSolver<'tcx>>>,
|
||||
}
|
||||
|
||||
/// The current state of the proof tree builder, at most places
|
||||
@ -118,36 +114,46 @@ pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> {
|
||||
Nested { is_normalizes_to_hack: IsNormalizesToHack },
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
pub(in crate::solve) enum WipCanonicalGoalEvaluationKind {
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<'tcx> {
|
||||
Overflow,
|
||||
CacheHit(inspect::CacheHit),
|
||||
CycleInStack,
|
||||
Interned { revisions: &'tcx [inspect::GoalEvaluationStep<'tcx>] },
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for WipCanonicalGoalEvaluationKind<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Overflow => write!(f, "Overflow"),
|
||||
Self::CycleInStack => write!(f, "CycleInStack"),
|
||||
Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct WipCanonicalGoalEvaluation<'tcx> {
|
||||
goal: CanonicalInput<'tcx>,
|
||||
kind: Option<WipCanonicalGoalEvaluationKind>,
|
||||
kind: Option<WipCanonicalGoalEvaluationKind<'tcx>>,
|
||||
/// Only used for uncached goals. After we finished evaluating
|
||||
/// the goal, this is interned and moved into `kind`.
|
||||
revisions: Vec<WipGoalEvaluationStep<'tcx>>,
|
||||
result: Option<QueryResult<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
|
||||
fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
|
||||
let kind = match self.kind {
|
||||
Some(WipCanonicalGoalEvaluationKind::Overflow) => {
|
||||
assert!(self.revisions.is_empty());
|
||||
let kind = match self.kind.unwrap() {
|
||||
WipCanonicalGoalEvaluationKind::Overflow => {
|
||||
inspect::CanonicalGoalEvaluationKind::Overflow
|
||||
}
|
||||
Some(WipCanonicalGoalEvaluationKind::CacheHit(hit)) => {
|
||||
inspect::CanonicalGoalEvaluationKind::CacheHit(hit)
|
||||
WipCanonicalGoalEvaluationKind::CycleInStack => {
|
||||
inspect::CanonicalGoalEvaluationKind::CycleInStack
|
||||
}
|
||||
WipCanonicalGoalEvaluationKind::Interned { revisions } => {
|
||||
inspect::CanonicalGoalEvaluationKind::Evaluation { revisions }
|
||||
}
|
||||
None => inspect::CanonicalGoalEvaluationKind::Uncached {
|
||||
revisions: self
|
||||
.revisions
|
||||
.into_iter()
|
||||
.map(WipGoalEvaluationStep::finalize)
|
||||
.collect(),
|
||||
},
|
||||
};
|
||||
|
||||
inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
|
||||
@ -226,33 +232,20 @@ impl<'tcx> WipProbeStep<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
fn new(
|
||||
state: impl Into<DebugSolver<'tcx>>,
|
||||
use_global_cache: UseGlobalCache,
|
||||
) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder {
|
||||
state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })),
|
||||
}
|
||||
fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder { state: Some(Box::new(state.into())) }
|
||||
}
|
||||
|
||||
fn nested<T: Into<DebugSolver<'tcx>>>(&self, state: impl FnOnce() -> T) -> Self {
|
||||
match &self.state {
|
||||
Some(prev_state) => Self {
|
||||
state: Some(Box::new(BuilderData {
|
||||
tree: state().into(),
|
||||
use_global_cache: prev_state.use_global_cache,
|
||||
})),
|
||||
},
|
||||
None => Self { state: None },
|
||||
}
|
||||
ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) }
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> {
|
||||
self.state.as_mut().map(|boxed| &mut boxed.tree)
|
||||
self.state.as_deref_mut()
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> {
|
||||
match self.state?.tree {
|
||||
match *self.state? {
|
||||
DebugSolver::GoalEvaluation(wip_goal_evaluation) => {
|
||||
Some(wip_goal_evaluation.finalize())
|
||||
}
|
||||
@ -260,13 +253,6 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_global_cache(&self) -> bool {
|
||||
self.state
|
||||
.as_ref()
|
||||
.map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes))
|
||||
.unwrap_or(true)
|
||||
}
|
||||
|
||||
pub fn new_maybe_root(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
generate_proof_tree: GenerateProofTree,
|
||||
@ -276,10 +262,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
GenerateProofTree::IfEnabled => {
|
||||
let opts = &tcx.sess.opts.unstable_opts;
|
||||
match opts.dump_solver_proof_tree {
|
||||
DumpSolverProofTree::Always => {
|
||||
let use_cache = opts.dump_solver_proof_tree_use_cache.unwrap_or(true);
|
||||
ProofTreeBuilder::new_root(UseGlobalCache::from_bool(use_cache))
|
||||
}
|
||||
DumpSolverProofTree::Always => ProofTreeBuilder::new_root(),
|
||||
// `OnError` is handled by reevaluating goals in error
|
||||
// reporting with `GenerateProofTree::Yes`.
|
||||
DumpSolverProofTree::OnError | DumpSolverProofTree::Never => {
|
||||
@ -287,12 +270,12 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
GenerateProofTree::Yes(use_cache) => ProofTreeBuilder::new_root(use_cache),
|
||||
GenerateProofTree::Yes => ProofTreeBuilder::new_root(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder::new(DebugSolver::Root, use_global_cache)
|
||||
pub fn new_root() -> ProofTreeBuilder<'tcx> {
|
||||
ProofTreeBuilder::new(DebugSolver::Root)
|
||||
}
|
||||
|
||||
pub fn new_noop() -> ProofTreeBuilder<'tcx> {
|
||||
@ -336,9 +319,27 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn finalize_evaluation(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
) -> Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]> {
|
||||
self.as_mut().map(|this| match this {
|
||||
DebugSolver::CanonicalGoalEvaluation(evaluation) => {
|
||||
let revisions = mem::take(&mut evaluation.revisions)
|
||||
.into_iter()
|
||||
.map(WipGoalEvaluationStep::finalize);
|
||||
let revisions = &*tcx.arena.alloc_from_iter(revisions);
|
||||
let kind = WipCanonicalGoalEvaluationKind::Interned { revisions };
|
||||
assert_eq!(evaluation.kind.replace(kind), None);
|
||||
revisions
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, canonical_goal_evaluation.state.unwrap().tree) {
|
||||
match (this, *canonical_goal_evaluation.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::GoalEvaluation(goal_evaluation),
|
||||
DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
|
||||
@ -348,7 +349,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) {
|
||||
pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match this {
|
||||
DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
|
||||
@ -372,7 +373,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, goal_evaluation.state.unwrap().tree) {
|
||||
match (this, *goal_evaluation.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation {
|
||||
evaluations, ..
|
||||
@ -396,7 +397,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
}
|
||||
pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, goal_evaluation_step.state.unwrap().tree) {
|
||||
match (this, *goal_evaluation_step.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations),
|
||||
DebugSolver::GoalEvaluationStep(goal_evaluation_step),
|
||||
@ -444,7 +445,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
|
||||
pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, probe.state.unwrap().tree) {
|
||||
match (this, *probe.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::Probe(WipProbe { steps, .. })
|
||||
| DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
||||
@ -486,7 +487,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
|
||||
|
||||
pub fn added_goals_evaluation(&mut self, added_goals_evaluation: ProofTreeBuilder<'tcx>) {
|
||||
if let Some(this) = self.as_mut() {
|
||||
match (this, added_goals_evaluation.state.unwrap().tree) {
|
||||
match (this, *added_goals_evaluation.state.unwrap()) {
|
||||
(
|
||||
DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
|
||||
evaluation: WipProbe { steps, .. },
|
||||
|
@ -38,9 +38,7 @@ mod project_goals;
|
||||
mod search_graph;
|
||||
mod trait_goals;
|
||||
|
||||
pub use eval_ctxt::{
|
||||
EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache,
|
||||
};
|
||||
pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt};
|
||||
pub use fulfill::FulfillmentCtxt;
|
||||
pub(crate) use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
|
||||
|
||||
|
@ -6,7 +6,6 @@ use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::dep_graph::dep_kinds;
|
||||
use rustc_middle::traits::solve::inspect::CacheHit;
|
||||
use rustc_middle::traits::solve::CacheData;
|
||||
use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
@ -191,8 +190,8 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
};
|
||||
|
||||
// Try to fetch the goal from the global cache.
|
||||
if inspect.use_global_cache() {
|
||||
if let Some(CacheData { result, reached_depth, encountered_overflow }) =
|
||||
'global: {
|
||||
let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) =
|
||||
self.global_cache(tcx).get(
|
||||
tcx,
|
||||
input,
|
||||
@ -201,13 +200,26 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
},
|
||||
available_depth,
|
||||
)
|
||||
{
|
||||
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
|
||||
CacheHit::Global,
|
||||
));
|
||||
self.on_cache_hit(reached_depth, encountered_overflow);
|
||||
return result;
|
||||
else {
|
||||
break 'global;
|
||||
};
|
||||
|
||||
// If we're building a proof tree and the current cache entry does not
|
||||
// contain a proof tree, we do not use the entry but instead recompute
|
||||
// the goal. We simply overwrite the existing entry once we're done,
|
||||
// caching the proof tree.
|
||||
if !inspect.is_noop() {
|
||||
if let Some(revisions) = proof_tree {
|
||||
inspect.goal_evaluation_kind(
|
||||
inspect::WipCanonicalGoalEvaluationKind::Interned { revisions },
|
||||
);
|
||||
} else {
|
||||
break 'global;
|
||||
}
|
||||
}
|
||||
|
||||
self.on_cache_hit(reached_depth, encountered_overflow);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check whether we're in a cycle.
|
||||
@ -238,9 +250,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
// Finally we can return either the provisional response for that goal if we have a
|
||||
// coinductive cycle or an ambiguous result if the cycle is inductive.
|
||||
Entry::Occupied(entry) => {
|
||||
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
|
||||
CacheHit::Provisional,
|
||||
));
|
||||
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack);
|
||||
|
||||
let stack_depth = *entry.get();
|
||||
debug!("encountered cycle with depth {stack_depth:?}");
|
||||
@ -329,6 +339,8 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
(current_entry, result)
|
||||
});
|
||||
|
||||
let proof_tree = inspect.finalize_evaluation(tcx);
|
||||
|
||||
// We're now done with this goal. In case this goal is involved in a larger cycle
|
||||
// do not remove it from the provisional cache and update its provisional result.
|
||||
// We only add the root of cycles to the global cache.
|
||||
@ -346,7 +358,9 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||
// more details.
|
||||
let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len();
|
||||
self.global_cache(tcx).insert(
|
||||
tcx,
|
||||
input,
|
||||
proof_tree,
|
||||
reached_depth,
|
||||
final_entry.encountered_overflow,
|
||||
final_entry.cycle_participants,
|
||||
|
@ -8,7 +8,7 @@ mod type_err_ctxt_ext;
|
||||
|
||||
use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache};
|
||||
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
@ -173,7 +173,7 @@ pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &
|
||||
infcx.probe(|_| {
|
||||
let goal = Goal { predicate: o.predicate, param_env: o.param_env };
|
||||
let tree = infcx
|
||||
.evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No))
|
||||
.evaluate_root_goal(goal, GenerateProofTree::Yes)
|
||||
.1
|
||||
.expect("proof tree should have been generated");
|
||||
let mut lock = std::io::stdout().lock();
|
||||
|
@ -155,12 +155,18 @@ pub trait Sized {
|
||||
/// Those implementations are:
|
||||
///
|
||||
/// - Arrays `[T; N]` implement `Unsize<[T]>`.
|
||||
/// - Types implementing a trait `Trait` also implement `Unsize<dyn Trait>`.
|
||||
/// - Structs `Foo<..., T, ...>` implement `Unsize<Foo<..., U, ...>>` if all of these conditions
|
||||
/// are met:
|
||||
/// - `T: Unsize<U>`.
|
||||
/// - Only the last field of `Foo` has a type involving `T`.
|
||||
/// - `Bar<T>: Unsize<Bar<U>>`, where `Bar<T>` stands for the actual type of that last field.
|
||||
/// - A type implements `Unsize<dyn Trait + 'a>` if all of these conditions are met:
|
||||
/// - The type implements `Trait`.
|
||||
/// - `Trait` is object safe.
|
||||
/// - The type is sized.
|
||||
/// - The type outlives `'a`.
|
||||
/// - Structs `Foo<..., T1, ..., Tn, ...>` implement `Unsize<Foo<..., U1, ..., Un, ...>>`
|
||||
/// where any number of (type and const) parameters may be changed if all of these conditions
|
||||
/// are met:
|
||||
/// - Only the last field of `Foo` has a type involving the parameters `T1`, ..., `Tn`.
|
||||
/// - All other parameters of the struct are equal.
|
||||
/// - `Field<T1, ..., Tn>: Unsize<Field<U1, ..., Un>>`, where `Field<...>` stands for the actual
|
||||
/// type of the struct's last field.
|
||||
///
|
||||
/// `Unsize` is used along with [`ops::CoerceUnsized`] to allow
|
||||
/// "user-defined" containers such as [`Rc`] to contain dynamically-sized
|
||||
|
@ -1294,13 +1294,63 @@ impl<T> SizedTypeProperties for T {}
|
||||
///
|
||||
/// Structs, enums, unions and tuples are supported.
|
||||
///
|
||||
/// Nested field accesses may be used, but not array indexes like in `C`'s `offsetof`.
|
||||
/// Nested field accesses may be used, but not array indexes.
|
||||
///
|
||||
/// Enum variants may be traversed as if they were fields. Variants themselves do
|
||||
/// not have an offset.
|
||||
///
|
||||
/// Visibility is respected - all types and fields must be visible to the call site:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(offset_of)]
|
||||
///
|
||||
/// mod nested {
|
||||
/// #[repr(C)]
|
||||
/// pub struct Struct {
|
||||
/// private: u8,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // assert_eq!(mem::offset_of!(nested::Struct, private), 0);
|
||||
/// // ^^^ error[E0616]: field `private` of struct `Struct` is private
|
||||
/// ```
|
||||
///
|
||||
/// Note that type layout is, in general, [subject to change and
|
||||
/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html).
|
||||
/// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If
|
||||
/// layout stability is required, consider using an [explicit `repr` attribute].
|
||||
///
|
||||
/// Rust guarantees that the offset of a given field within a given type will not
|
||||
/// change over the lifetime of the program. However, two different compilations of
|
||||
/// the same program may result in different layouts. Also, even within a single
|
||||
/// program execution, no guarantees are made about types which are *similar* but
|
||||
/// not *identical*, e.g.:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(offset_of)]
|
||||
///
|
||||
/// struct Wrapper<T, U>(T, U);
|
||||
///
|
||||
/// type A = Wrapper<u8, u8>;
|
||||
/// type B = Wrapper<u8, i8>;
|
||||
///
|
||||
/// // Not necessarily identical even though `u8` and `i8` have the same layout!
|
||||
/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(B, 1));
|
||||
///
|
||||
/// #[repr(transparent)]
|
||||
/// struct U8(u8);
|
||||
///
|
||||
/// type C = Wrapper<u8, U8>;
|
||||
///
|
||||
/// // Not necessarily identical even though `u8` and `U8` have the same layout!
|
||||
/// // assert!(mem::offset_of!(A, 1), mem::offset_of!(C, 1));
|
||||
///
|
||||
/// struct Empty<T>(core::marker::PhantomData<T>);
|
||||
///
|
||||
/// // Not necessarily identical even though `PhantomData` always has the same layout!
|
||||
/// // assert!(mem::offset_of!(Empty<u8>, 0), mem::offset_of!(Empty<i8>, 0));
|
||||
/// ```
|
||||
///
|
||||
/// [explicit `repr` attribute]: https://doc.rust-lang.org/reference/type-layout.html#representations
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@ -1329,6 +1379,17 @@ impl<T> SizedTypeProperties for T {}
|
||||
///
|
||||
/// assert_eq!(mem::offset_of!(NestedA, b.0), 0);
|
||||
///
|
||||
/// #[repr(u8)]
|
||||
/// enum Enum {
|
||||
/// A(u8, u16),
|
||||
/// B { one: u8, two: u16 },
|
||||
/// }
|
||||
///
|
||||
/// # #[cfg(not(bootstrap))]
|
||||
/// assert_eq!(mem::offset_of!(Enum, A.0), 1);
|
||||
/// # #[cfg(not(bootstrap))]
|
||||
/// assert_eq!(mem::offset_of!(Enum, B.two), 2);
|
||||
///
|
||||
/// # #[cfg(not(bootstrap))]
|
||||
/// assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
|
||||
/// ```
|
||||
|
@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
|
||||
const ENTRY_LIMIT: usize = 900;
|
||||
// FIXME: The following limits should be reduced eventually.
|
||||
const ISSUES_ENTRY_LIMIT: usize = 1854;
|
||||
const ROOT_ENTRY_LIMIT: usize = 866;
|
||||
const ROOT_ENTRY_LIMIT: usize = 867;
|
||||
|
||||
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
|
||||
"rs", // test source files
|
||||
|
@ -4,7 +4,7 @@ include ../tools.mk
|
||||
# Ensure that crates compiled with different rustc versions cannot
|
||||
# be dynamically linked.
|
||||
|
||||
FLAGS := -Cprefer-dynamic -Zsymbol-mangling-version=v0
|
||||
FLAGS := -Cprefer-dynamic -Csymbol-mangling-version=v0
|
||||
UNAME := $(shell uname)
|
||||
ifeq ($(UNAME),Linux)
|
||||
EXT=".so"
|
||||
|
2
tests/ui/symbol-mangling-version/bad-value.bad.stderr
Normal file
2
tests/ui/symbol-mangling-version/bad-value.bad.stderr
Normal file
@ -0,0 +1,2 @@
|
||||
error: incorrect value `bad-value` for codegen option `symbol-mangling-version` - either `legacy` or `v0` (RFC 2603) was expected
|
||||
|
2
tests/ui/symbol-mangling-version/bad-value.blank.stderr
Normal file
2
tests/ui/symbol-mangling-version/bad-value.blank.stderr
Normal file
@ -0,0 +1,2 @@
|
||||
error: incorrect value `` for codegen option `symbol-mangling-version` - either `legacy` or `v0` (RFC 2603) was expected
|
||||
|
@ -0,0 +1,2 @@
|
||||
error: codegen option `symbol-mangling-version` requires either `legacy` or `v0` (RFC 2603) (C symbol-mangling-version=<value>)
|
||||
|
6
tests/ui/symbol-mangling-version/bad-value.rs
Normal file
6
tests/ui/symbol-mangling-version/bad-value.rs
Normal file
@ -0,0 +1,6 @@
|
||||
// revisions: no-value blank bad
|
||||
// [no-value] compile-flags: -Csymbol-mangling-version
|
||||
// [blank] compile-flags: -Csymbol-mangling-version=
|
||||
// [bad] compile-flags: -Csymbol-mangling-version=bad-value
|
||||
|
||||
fn main() {}
|
5
tests/ui/symbol-mangling-version/stable.rs
Normal file
5
tests/ui/symbol-mangling-version/stable.rs
Normal file
@ -0,0 +1,5 @@
|
||||
// check-pass
|
||||
// revisions: v0
|
||||
// [v0] compile-flags: -Csymbol-mangling-version=v0
|
||||
|
||||
fn main() {}
|
2
tests/ui/symbol-mangling-version/unstable.legacy.stderr
Normal file
2
tests/ui/symbol-mangling-version/unstable.legacy.stderr
Normal file
@ -0,0 +1,2 @@
|
||||
error: `-C symbol-mangling-version=legacy` requires `-Z unstable-options`
|
||||
|
6
tests/ui/symbol-mangling-version/unstable.rs
Normal file
6
tests/ui/symbol-mangling-version/unstable.rs
Normal file
@ -0,0 +1,6 @@
|
||||
// revisions: legacy legacy-ok
|
||||
// [legacy] compile-flags: -Csymbol-mangling-version=legacy
|
||||
// [legacy-ok] check-pass
|
||||
// [legacy-ok] compile-flags: -Zunstable-options -Csymbol-mangling-version=legacy
|
||||
|
||||
fn main() {}
|
29
tests/ui/unsized/unsize-coerce-multiple-adt-params.rs
Normal file
29
tests/ui/unsized/unsize-coerce-multiple-adt-params.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// check-pass
|
||||
|
||||
struct Foo<T, U>
|
||||
where
|
||||
(T, U): Trait,
|
||||
{
|
||||
f: <(T, U) as Trait>::Assoc,
|
||||
}
|
||||
|
||||
trait Trait {
|
||||
type Assoc: ?Sized;
|
||||
}
|
||||
|
||||
struct Count<const N: usize>;
|
||||
|
||||
impl<const N: usize> Trait for (i32, Count<N>) {
|
||||
type Assoc = [(); N];
|
||||
}
|
||||
|
||||
impl<'a> Trait for (u32, ()) {
|
||||
type Assoc = [()];
|
||||
}
|
||||
|
||||
// Test that we can unsize several trait params in creative ways.
|
||||
fn unsize<const N: usize>(x: &Foo<i32, Count<N>>) -> &Foo<u32, ()> {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {}
|
Loading…
x
Reference in New Issue
Block a user