Skip to content
Merged
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
207 changes: 72 additions & 135 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Large diffs are not rendered by default.

68 changes: 18 additions & 50 deletions compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use
//! when specifying impls to be derived.

use std::iter::once;

pub(crate) use Ty::*;
use rustc_ast::{self as ast, GenericArg, GenericParamKind, Generics, TyKind};
use rustc_ast::{self as ast, GenericArg, TyKind};
use rustc_expand::base::ExtCtxt;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
use thin_vec::ThinVec;
Expand Down Expand Up @@ -33,21 +35,17 @@ impl Path {
Path { path, params, kind }
}

pub(crate) fn to_path(
&self,
cx: &ExtCtxt<'_>,
span: Span,
self_ty: Ident,
self_generics: &Generics,
) -> ast::Path {
let mut idents = self.path.iter().map(|s| Ident::new(*s, span)).collect::<Vec<_>>();
let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics));
pub(crate) fn to_path(&self, cx: &ExtCtxt<'_>, span: Span) -> ast::Path {
let idents = self.path.iter().map(|s| Ident::new(*s, span));
let tys = self.params.iter().map(|t| t.to_ty(cx, span));
let params = tys.map(GenericArg::Type).collect();

if let PathKind::Std = self.kind {
let idents = if let PathKind::Std = self.kind {
let def_site = cx.with_def_site_ctxt(DUMMY_SP);
idents.insert(0, Ident::new(kw::DollarCrate, def_site));
}
once(Ident::new(kw::DollarCrate, def_site)).chain(idents).collect()
} else {
idents.collect()
};
cx.path_all(span, false, idents, params)
}
}
Expand All @@ -72,20 +70,14 @@ pub(crate) fn self_ref() -> Ty {
}

impl Ty {
pub(crate) fn to_ty(
&self,
cx: &ExtCtxt<'_>,
span: Span,
self_ty: Ident,
self_generics: &Generics,
) -> Box<ast::Ty> {
pub(crate) fn to_ty(&self, cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Ty> {
match self {
Ref(ty, mutbl) => {
let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
let raw_ty = ty.to_ty(cx, span);
cx.ty_ref(span, raw_ty, None, *mutbl)
}
Path(p) => cx.ty_path(p.to_path(cx, span, self_ty, self_generics)),
Self_ => cx.ty_path(self.to_path(cx, span, self_ty, self_generics)),
Path(p) => cx.ty_path(p.to_path(cx, span)),
Self_ => cx.ty_path(self.to_path(cx, span)),
Unit => {
let ty = ast::TyKind::Tup(ThinVec::new());
cx.ty(span, ty)
Expand All @@ -94,34 +86,10 @@ impl Ty {
}
}

pub(crate) fn to_path(
&self,
cx: &ExtCtxt<'_>,
span: Span,
self_ty: Ident,
generics: &Generics,
) -> ast::Path {
pub(crate) fn to_path(&self, cx: &ExtCtxt<'_>, span: Span) -> ast::Path {
match self {
Self_ => {
let params: Vec<_> = generics
.params
.iter()
.map(|param| match param.kind {
GenericParamKind::Lifetime => {
GenericArg::Lifetime(ast::Lifetime { id: param.id, ident: param.ident })
}
GenericParamKind::Type { .. } => {
GenericArg::Type(cx.ty_ident(span, param.ident))
}
GenericParamKind::Const { .. } => {
GenericArg::Const(cx.const_ident(span, param.ident))
}
})
.collect();

cx.path_all(span, false, vec![self_ty], params)
}
Path(p) => p.to_path(cx, span, self_ty, generics),
Self_ => cx.path_ident(span, Ident::new(kw::SelfUpper, span)),
Path(p) => p.to_path(cx, span),
AstTy(ty) => match &ty.kind {
TyKind::Path(_, path) => path.clone(),
_ => cx.dcx().span_bug(span, "non-path in a path in generic `derive`"),
Expand Down
32 changes: 13 additions & 19 deletions compiler/rustc_builtin_macros/src/deriving/ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,23 @@ pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) ->
// ::core::cmp::Ord::cmp(&self.y, &other.y),
// cmp => cmp,
// }
let expr = cs_fold(
// foldr nests the if-elses correctly, leaving the first field
// as the outermost one, and the last as the innermost.
false,
let expr = cs_foldr(
cx,
span,
substr,
|cx, fold| match fold {
CsFold::Single(field) => {
let [other_expr] = &field.other_selflike_exprs[..] else {
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
};
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
cx.expr_call_global(field.span, cmp_path.clone(), args)
}
CsFold::Combine(span, expr1, expr2) => {
let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1);
let neq_arm =
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm])
}
CsFold::Fieldless => cx.expr_path(equal_path.clone()),
|field| {
let [other_expr] = &field.other_selflike_exprs[..] else {
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
};
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
cx.expr_call_global(field.span, cmp_path.clone(), args)
},
|span, expr1, expr2| {
let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1);
let neq_arm = cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm])
},
|| cx.expr_path(equal_path.clone()),
);
BlockOrExpr::new_expr(expr)
}
113 changes: 52 additions & 61 deletions compiler/rustc_builtin_macros/src/deriving/partial_ord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,73 +124,64 @@ fn cs_partial_cmp(
// ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y),
// cmp => cmp,
// }
let expr = cs_fold(
// foldr nests the if-elses correctly, leaving the first field
// as the outermost one, and the last as the innermost.
false,
let expr = cs_foldr(
cx,
span,
substr,
|cx, fold| match fold {
CsFold::Single(field) => {
let [other_expr] = &field.other_selflike_exprs[..] else {
cx.dcx()
.span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`");
};
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
}
CsFold::Combine(span, mut expr1, expr2) => {
// When the item is an enum, this expands to
// ```
// match (expr2) {
// Some(Ordering::Equal) => expr1,
// cmp => cmp
// }
// ```
// where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match`
// against the enum variants. This means that we begin by comparing the enum discriminants,
// before either inspecting their contents (if they match), or returning
// the `cmp::Ordering` of comparing the enum discriminants.
// ```
// match partial_cmp(self_discr, other_discr) {
// Some(Ordering::Equal) => match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0),
// _ => Some(Ordering::Equal)
// }
// cmp => cmp
// }
// ```
// If we have any certain enum layouts, flipping this results in better codegen
// ```
// match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// _ => partial_cmp(self_discr, other_discr)
// }
// ```
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
|field| {
let [other_expr] = &field.other_selflike_exprs[..] else {
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`");
};
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
},
|span, mut expr1, expr2| {
// When the item is an enum, this expands to
// ```
// match (expr2) {
// Some(Ordering::Equal) => expr1,
// cmp => cmp
// }
// ```
// where `expr2` is `partial_cmp(self_discr, other_discr)`, and `expr1` is a `match`
// against the enum variants. This means that we begin by comparing the enum discriminants,
// before either inspecting their contents (if they match), or returning
// the `cmp::Ordering` of comparing the enum discriminants.
// ```
// match partial_cmp(self_discr, other_discr) {
// Some(Ordering::Equal) => match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0),
// _ => Some(Ordering::Equal)
// }
// cmp => cmp
// }
// ```
// If we have any certain enum layouts, flipping this results in better codegen
// ```
// match (self, other) {
// (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
// _ => partial_cmp(self_discr, other_discr)
// }
// ```
// Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354

if !discr_then_data
&& let ExprKind::Match(_, arms, _) = &mut expr1.kind
&& let Some(last) = arms.last_mut()
&& let PatKind::Wild = last.pat.kind
{
last.body = Some(expr2);
expr1
} else {
let eq_arm = cx.arm(
span,
cx.pat_some(span, cx.pat_path(span, equal_path.clone())),
expr1,
);
let neq_arm =
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm])
}
if !discr_then_data
&& let ExprKind::Match(_, arms, _) = &mut expr1.kind
&& let Some(last) = arms.last_mut()
&& let PatKind::Wild = last.pat.kind
{
last.body = Some(expr2);
expr1
} else {
let eq_arm =
cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
let neq_arm =
cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
cx.expr_match(span, expr2, thin_vec![eq_arm, neq_arm])
}
CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
},
|| cx.expr_some(span, cx.expr_path(equal_path.clone())),
);
BlockOrExpr::new_expr(expr)
}
3 changes: 1 addition & 2 deletions tests/ui/const-generics/mgca/const-ctor-overflow-eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ struct U;

#[derive(ConstParamTy, PartialEq, Eq)]
//~^ ERROR overflow evaluating whether `S<U>` is well-formed
//~| ERROR overflow evaluating whether `S<U>` is well-formed

struct S<const N: U>()
where
S<{ U }>:;
//~^ ERROR overflow evaluating whether `S<U>` is well-formed
//~| ERROR overflow evaluating whether `S<U>` is well-formed
//~| ERROR overflow evaluating whether `S<U>` is well-formed
//~| ERROR overflow evaluating whether `S<U>` is well-formed

fn main() {}
23 changes: 12 additions & 11 deletions tests/ui/const-generics/mgca/const-ctor-overflow-eval.stderr
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
error[E0275]: overflow evaluating whether `S<U>` is well-formed
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | S<{ U }>:;
| ^^^^^^^^
|
note: required by a bound in `S`
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | struct S<const N: U>()
| - required by a bound in this struct
Expand All @@ -14,13 +14,13 @@ LL | S<{ U }>:;
| ^^^^^^^^ required by this bound in `S`

error[E0275]: overflow evaluating whether `S<U>` is well-formed
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | S<{ U }>:;
| ^^^^^^^^
|
note: required by a bound in `S`
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | struct S<const N: U>()
| - required by a bound in this struct
Expand All @@ -30,13 +30,13 @@ LL | S<{ U }>:;
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0275]: overflow evaluating whether `S<U>` is well-formed
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | S<{ U }>:;
| ^^^^^^^^
|
note: required by a bound in `S`
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | struct S<const N: U>()
| - required by a bound in this struct
Expand All @@ -52,7 +52,7 @@ LL | #[derive(ConstParamTy, PartialEq, Eq)]
| ^^^^^^^^^
|
note: required by a bound in `S`
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | struct S<const N: U>()
| - required by a bound in this struct
Expand All @@ -61,19 +61,20 @@ LL | S<{ U }>:;
| ^^^^^^^^ required by this bound in `S`

error[E0275]: overflow evaluating whether `S<U>` is well-formed
--> $DIR/const-ctor-overflow-eval.rs:8:35
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | #[derive(ConstParamTy, PartialEq, Eq)]
| ^^
LL | S<{ U }>:;
| ^^^^^^^^
|
note: required by a bound in `S`
--> $DIR/const-ctor-overflow-eval.rs:14:5
--> $DIR/const-ctor-overflow-eval.rs:12:5
|
LL | struct S<const N: U>()
| - required by a bound in this struct
LL | where
LL | S<{ U }>:;
| ^^^^^^^^ required by this bound in `S`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 5 previous errors

Expand Down
Loading
Loading