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 6 pull requests #115925

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .mailmap
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,8 @@ Timothy Maloney <tmaloney@pdx.edu>
Tomas Koutsky <tomas@stepnivlk.net>
Torsten Weber <TorstenWeber12@gmail.com>
Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
Tshepang Mbambo <tshepang@gmail.com>
Ty Overby <ty@pre-alpha.com>
Expand Down
17 changes: 14 additions & 3 deletions compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ pub fn expand_deriving_eq(
is_const: bool,
) {
let span = cx.with_def_site_ctxt(span);

let structural_trait_def = TraitDef {
span,
path: path_std!(marker::StructuralEq),
skip_path_as_bound: true, // crucial!
needs_copy_as_bound_if_packed: false,
additional_bounds: Vec::new(),
supports_unions: true,
methods: Vec::new(),
associated_types: Vec::new(),
is_const: false,
};
structural_trait_def.expand(cx, mitem, item, push);

let trait_def = TraitDef {
span,
path: path_std!(cmp::Eq),
Expand All @@ -44,9 +58,6 @@ pub fn expand_deriving_eq(
associated_types: Vec::new(),
is_const,
};

super::inject_impl_of_structural_trait(cx, span, item, path_std!(marker::StructuralEq), push);

trait_def.expand_ext(cx, mitem, item, push, true)
}

Expand Down
19 changes: 13 additions & 6 deletions compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,20 @@ pub fn expand_deriving_partial_eq(
BlockOrExpr::new_expr(expr)
}

super::inject_impl_of_structural_trait(
cx,
let structural_trait_def = TraitDef {
span,
item,
path_std!(marker::StructuralPartialEq),
push,
);
path: path_std!(marker::StructuralPartialEq),
skip_path_as_bound: true, // crucial!
needs_copy_as_bound_if_packed: false,
additional_bounds: Vec::new(),
// We really don't support unions, but that's already checked by the impl generated below;
// a second check here would lead to redundant error messages.
supports_unions: true,
methods: Vec::new(),
associated_types: Vec::new(),
is_const: false,
};
structural_trait_def.expand(cx, mitem, item, push);

// No need to generate `ne`, the default suffices, and not generating it is
// faster.
Expand Down
22 changes: 13 additions & 9 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,9 @@ impl<'a> TraitDef<'a> {
.collect();

// Require the current trait.
bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
if !self.skip_path_as_bound {
bounds.push(cx.trait_bound(trait_path.clone(), self.is_const));
}

// Add a `Copy` bound if required.
if is_packed && self.needs_copy_as_bound_if_packed {
Expand All @@ -722,15 +724,17 @@ impl<'a> TraitDef<'a> {
));
}

let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_generic_params: field_ty_param.bound_generic_params,
bounded_ty: field_ty_param.ty,
bounds,
};
if !bounds.is_empty() {
let predicate = ast::WhereBoundPredicate {
span: self.span,
bound_generic_params: field_ty_param.bound_generic_params,
bounded_ty: field_ty_param.ty,
bounds,
};

let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
let predicate = ast::WherePredicate::BoundPredicate(predicate);
where_clause.predicates.push(predicate);
}
}
}
}
Expand Down
98 changes: 2 additions & 96 deletions compiler/rustc_builtin_macros/src/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem};
use rustc_ast::{GenericArg, MetaItem};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use thin_vec::{thin_vec, ThinVec};

Expand Down Expand Up @@ -116,100 +116,6 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
}))
}

// Injects `impl<...> Structural for ItemType<...> { }`. In particular,
// does *not* add `where T: Structural` for parameters `T` in `...`.
// (That's the main reason we cannot use TraitDef here.)
fn inject_impl_of_structural_trait(
cx: &mut ExtCtxt<'_>,
span: Span,
item: &Annotatable,
structural_path: generic::ty::Path,
push: &mut dyn FnMut(Annotatable),
) {
let Annotatable::Item(item) = item else {
unreachable!();
};

let generics = match &item.kind {
ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics,
// Do not inject `impl Structural for Union`. (`PartialEq` does not
// support unions, so we will see error downstream.)
ItemKind::Union(..) => return,
_ => unreachable!(),
};

// Create generics param list for where clauses and impl headers
let mut generics = generics.clone();

let ctxt = span.ctxt();

// Create the type of `self`.
//
// in addition, remove defaults from generic params (impls cannot have them).
let self_params: Vec<_> = generics
.params
.iter_mut()
.map(|param| match &mut param.kind {
ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident),
),
ast::GenericParamKind::Type { default } => {
*default = None;
ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
}
ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
*default = None;
ast::GenericArg::Const(
cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident),
)
}
})
.collect();

let type_ident = item.ident;

let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics));
let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params));

// It would be nice to also encode constraint `where Self: Eq` (by adding it
// onto `generics` cloned above). Unfortunately, that strategy runs afoul of
// rust-lang/rust#48214. So we perform that additional check in the compiler
// itself, instead of encoding it here.

// Keep the lint and stability attributes of the original item, to control
// how the generated implementation is linted.
let mut attrs = ast::AttrVec::new();
attrs.extend(
item.attrs
.iter()
.filter(|a| {
[sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
.contains(&a.name_or_empty())
})
.cloned(),
);
// Mark as `automatically_derived` to avoid some silly lints.
attrs.push(cx.attr_word(sym::automatically_derived, span));

let newitem = cx.item(
span,
Ident::empty(),
attrs,
ItemKind::Impl(Box::new(Impl {
unsafety: ast::Unsafe::No,
polarity: ast::ImplPolarity::Positive,
defaultness: ast::Defaultness::Final,
constness: ast::Const::No,
generics,
of_trait: Some(trait_ref),
self_ty: self_type,
items: ThinVec::new(),
})),
);

push(Annotatable::Item(newitem));
}

fn assert_ty_bounds(
cx: &mut ExtCtxt<'_>,
stmts: &mut ThinVec<ast::Stmt>,
Expand Down
33 changes: 24 additions & 9 deletions compiler/rustc_middle/src/traits/solve/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,29 @@ pub enum CacheHit {
Global,
}

#[derive(Eq, PartialEq)]
pub enum GoalEvaluationKind {
Root,
Nested { is_normalizes_to_hack: IsNormalizesToHack },
}

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

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

#[derive(Eq, PartialEq)]
pub enum GoalEvaluationKind<'tcx> {
pub enum CanonicalGoalEvaluationKind<'tcx> {
Overflow,
CacheHit(CacheHit),
Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
Expand All @@ -52,22 +58,31 @@ pub struct GoalEvaluationStep<'tcx> {
pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,

/// The actual evaluation of the goal, always `ProbeKind::Root`.
pub evaluation: GoalCandidate<'tcx>,
pub evaluation: Probe<'tcx>,
}

/// A self-contained computation during trait solving. This either
/// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation
/// of a goal.
#[derive(Eq, PartialEq)]
pub struct GoalCandidate<'tcx> {
pub added_goals_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
pub candidates: Vec<GoalCandidate<'tcx>>,
pub struct Probe<'tcx> {
pub steps: Vec<ProbeStep<'tcx>>,
pub kind: ProbeKind<'tcx>,
}

impl Debug for GoalCandidate<'_> {
impl Debug for Probe<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
ProofTreeFormatter::new(f).format_candidate(self)
ProofTreeFormatter::new(f).format_probe(self)
}
}

#[derive(Eq, PartialEq)]
pub enum ProbeStep<'tcx> {
AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
EvaluateGoals(AddedGoalsEvaluation<'tcx>),
NestedProbe(Probe<'tcx>),
}

#[derive(Debug, PartialEq, Eq)]
pub enum ProbeKind<'tcx> {
/// The root inference context while proving a goal.
Expand Down
34 changes: 19 additions & 15 deletions compiler/rustc_middle/src/traits/solve/inspect/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
}

pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
let goal_text = match eval.is_normalizes_to_hack {
IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
IsNormalizesToHack::No => "GOAL",
let goal_text = match eval.kind {
GoalEvaluationKind::Root => "ROOT GOAL",
GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
IsNormalizesToHack::No => "GOAL",
IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
},
};
writeln!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))?;
Expand All @@ -68,16 +71,16 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
writeln!(self.f, "GOAL: {:?}", eval.goal)?;

match &eval.kind {
GoalEvaluationKind::Overflow => {
CanonicalGoalEvaluationKind::Overflow => {
writeln!(self.f, "OVERFLOW: {:?}", eval.result)
}
GoalEvaluationKind::CacheHit(CacheHit::Global) => {
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
}
GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
}
GoalEvaluationKind::Uncached { revisions } => {
CanonicalGoalEvaluationKind::Uncached { revisions } => {
for (n, step) in revisions.iter().enumerate() {
writeln!(self.f, "REVISION {n}")?;
self.nested(|this| this.format_evaluation_step(step))?;
Expand All @@ -92,11 +95,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
evaluation_step: &GoalEvaluationStep<'_>,
) -> std::fmt::Result {
writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
self.format_candidate(&evaluation_step.evaluation)
self.format_probe(&evaluation_step.evaluation)
}

pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
match &candidate.kind {
pub(super) fn format_probe(&mut self, probe: &Probe<'_>) -> std::fmt::Result {
match &probe.kind {
ProbeKind::Root { result } => {
writeln!(self.f, "ROOT RESULT: {result:?}")
}
Expand All @@ -118,11 +121,12 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
}?;

self.nested(|this| {
for candidate in &candidate.candidates {
this.format_candidate(candidate)?;
}
for nested in &candidate.added_goals_evaluations {
this.format_added_goals_evaluation(nested)?;
for step in &probe.steps {
match step {
ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
}
}
Ok(())
})
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,25 @@ macro_rules! nop_lift {
impl<'a, 'tcx> Lift<'tcx> for $ty {
type Lifted = $lifted;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
// Assert that the set has the right type.
// Given an argument that has an interned type, the return type has the type of
// the corresponding interner set. This won't actually return anything, we're
// just doing this to compute said type!
fn _intern_set_ty_from_interned_ty<'tcx, Inner>(
_x: Interned<'tcx, Inner>,
) -> InternedSet<'tcx, Inner> {
unreachable!()
}
fn _type_eq<T>(_x: &T, _y: &T) {}
fn _test<'tcx>(x: $lifted, tcx: TyCtxt<'tcx>) {
// If `x` is a newtype around an `Interned<T>`, then `interner` is an
// interner of appropriate type. (Ideally we'd also check that `x` is a
// newtype with just that one field. Not sure how to do that.)
let interner = _intern_set_ty_from_interned_ty(x.0);
// Now check that this is the same type as `interners.$set`.
_type_eq(&interner, &tcx.interners.$set);
}

tcx.interners
.$set
.contains_pointer_to(&InternedInSet(&*self.0.0))
Expand All @@ -1230,6 +1249,11 @@ macro_rules! nop_list_lift {
impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> {
type Lifted = &'tcx List<$lifted>;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
// Assert that the set has the right type.
if false {
let _x: &InternedSet<'tcx, List<$lifted>> = &tcx.interners.$set;
}

if self.is_empty() {
return Some(List::empty());
}
Expand Down
Loading
Loading