Skip to content

Commit 3d719f0

Browse files
committed
A working spaceship operator
1 parent a18bd8a commit 3d719f0

File tree

24 files changed

+302
-91
lines changed

24 files changed

+302
-91
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,10 @@ pub enum BinOpKind {
906906
Ge,
907907
/// The `>` operator (greater than)
908908
Gt,
909+
/// The `<=>` operator (Ord::cmp)
910+
Cmp,
911+
/// The `<==>` operator (PartialOrd::partial_cmp)
912+
CmpPartial,
909913
}
910914

911915
impl BinOpKind {
@@ -930,6 +934,8 @@ impl BinOpKind {
930934
Ne => "!=",
931935
Ge => ">=",
932936
Gt => ">",
937+
Cmp => "<=>",
938+
CmpPartial => "<==>",
933939
}
934940
}
935941

compiler/rustc_ast/src/util/parser.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ pub enum AssocOp {
4444
Greater,
4545
/// `>=`
4646
GreaterEqual,
47+
/// `<=>`
48+
Cmp,
49+
/// `<==>`
50+
CmpPartial,
4751
/// `=`
4852
Assign,
4953
/// `?=` where ? is one of the BinOpToken
@@ -112,6 +116,8 @@ impl AssocOp {
112116
BinOpKind::Ge => GreaterEqual,
113117
BinOpKind::Eq => Equal,
114118
BinOpKind::Ne => NotEqual,
119+
BinOpKind::Cmp => Cmp,
120+
BinOpKind::CmpPartial => CmpPartial,
115121
BinOpKind::Mul => Multiply,
116122
BinOpKind::Div => Divide,
117123
BinOpKind::Rem => Modulus,
@@ -138,7 +144,9 @@ impl AssocOp {
138144
BitAnd => ExprPrecedence::BitAnd,
139145
BitXor => ExprPrecedence::BitXor,
140146
BitOr => ExprPrecedence::BitOr,
141-
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
147+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | Cmp | CmpPartial => {
148+
ExprPrecedence::Compare
149+
}
142150
LAnd => ExprPrecedence::LAnd,
143151
LOr => ExprPrecedence::LOr,
144152
DotDot | DotDotEq => ExprPrecedence::Range,
@@ -154,16 +162,15 @@ impl AssocOp {
154162
Assign | AssignOp(_) => Fixity::Right,
155163
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
156164
| BitXor | BitOr | LAnd | LOr => Fixity::Left,
157-
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
158-
Fixity::None
159-
}
165+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | Cmp | CmpPartial
166+
| DotDot | DotDotEq => Fixity::None,
160167
}
161168
}
162169

163170
pub fn is_comparison(&self) -> bool {
164171
use AssocOp::*;
165172
match *self {
166-
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
173+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | Cmp | CmpPartial => true,
167174
Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
168175
| ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
169176
false
@@ -175,9 +182,9 @@ impl AssocOp {
175182
use AssocOp::*;
176183
match *self {
177184
Assign | AssignOp(_) => true,
178-
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
179-
| Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
180-
| BitOr | LAnd | LOr | DotDot | DotDotEq => false,
185+
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | Cmp | CmpPartial
186+
| As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight
187+
| BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => false,
181188
}
182189
}
183190

@@ -190,6 +197,8 @@ impl AssocOp {
190197
GreaterEqual => Some(BinOpKind::Ge),
191198
Equal => Some(BinOpKind::Eq),
192199
NotEqual => Some(BinOpKind::Ne),
200+
Cmp => Some(BinOpKind::Cmp),
201+
CmpPartial => Some(BinOpKind::CmpPartial),
193202
Multiply => Some(BinOpKind::Mul),
194203
Divide => Some(BinOpKind::Div),
195204
Modulus => Some(BinOpKind::Rem),

compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use rustc_ast::MetaItem;
1+
use rustc_ast::ptr::P;
2+
use rustc_ast::{BinOpKind, BorrowKind, Expr, ExprKind, MetaItem, Mutability};
23
use rustc_expand::base::{Annotatable, ExtCtxt};
34
use rustc_span::{Ident, Span, sym};
45
use thin_vec::thin_vec;
@@ -42,7 +43,6 @@ pub(crate) fn expand_deriving_ord(
4243
pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
4344
let test_id = Ident::new(sym::cmp, span);
4445
let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
45-
let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]);
4646

4747
// Builds:
4848
//
@@ -63,8 +63,23 @@ pub(crate) fn cs_cmp(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
6363
let [other_expr] = &field.other_selflike_exprs[..] else {
6464
cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`");
6565
};
66-
let args = thin_vec![field.self_expr.clone(), other_expr.clone()];
67-
cx.expr_call_global(field.span, cmp_path.clone(), args)
66+
let convert = |expr: &P<Expr>| {
67+
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = &expr.kind {
68+
if let ExprKind::Block(..) = &inner.kind {
69+
// `&{ x }` form: remove the `&`, add parens.
70+
cx.expr_paren(field.span, inner.clone())
71+
} else {
72+
// `&x` form: remove the `&`.
73+
inner.clone()
74+
}
75+
} else {
76+
cx.expr_deref(field.span, expr.clone())
77+
}
78+
};
79+
80+
let lhs = convert(&field.self_expr);
81+
let rhs = convert(&other_expr);
82+
cx.expr_binary(field.span, BinOpKind::Cmp, lhs, rhs)
6883
}
6984
CsFold::Combine(span, expr1, expr2) => {
7085
let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1);

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ language_item_table! {
277277
OrderingEnum, sym::Ordering, ordering_enum, Target::Enum, GenericRequirement::Exact(0);
278278
PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
279279
PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
280+
Ord, sym::Ord, ord_trait, Target::Trait, GenericRequirement::Exact(0);
280281
CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
281282

282283
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and

compiler/rustc_hir_typeck/src/op.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use rustc_data_structures::packed::Pu128;
44
use rustc_errors::codes::*;
55
use rustc_errors::{Applicability, Diag, struct_span_code_err};
6+
use rustc_hir::LangItem;
67
use rustc_infer::traits::ObligationCauseCode;
78
use rustc_middle::ty::adjustment::{
89
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
@@ -194,6 +195,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
194195
self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
195196
tcx.types.bool
196197
}
198+
199+
BinOpCategory::ThreeWayComparison => {
200+
// both LHS and RHS and result will have the same type
201+
self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
202+
tcx.ty_ordering_enum(Some(lhs_span))
203+
}
204+
205+
BinOpCategory::ThreeWayComparisonPartial => {
206+
// both LHS and RHS and result will have the same type
207+
self.demand_suptype(rhs_span, lhs_ty, rhs_ty);
208+
let ordering_ty = tcx.ty_ordering_enum(Some(lhs_span));
209+
Ty::new_lang_item(tcx, ordering_ty, LangItem::Option).unwrap()
210+
}
197211
}
198212
}
199213

@@ -1006,7 +1020,9 @@ fn lang_item_for_op(
10061020
| hir::BinOpKind::Eq
10071021
| hir::BinOpKind::Ne
10081022
| hir::BinOpKind::And
1009-
| hir::BinOpKind::Or => {
1023+
| hir::BinOpKind::Or
1024+
| hir::BinOpKind::Cmp
1025+
| hir::BinOpKind::CmpPartial => {
10101026
span_bug!(span, "impossible assignment operation: {}=", op.node.as_str())
10111027
}
10121028
}
@@ -1027,6 +1043,8 @@ fn lang_item_for_op(
10271043
hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()),
10281044
hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()),
10291045
hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()),
1046+
hir::BinOpKind::Cmp => (sym::cmp, lang.ord_trait()),
1047+
hir::BinOpKind::CmpPartial => (sym::partial_cmp, lang.partial_ord_trait()),
10301048
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
10311049
hir::BinOpKind::And | hir::BinOpKind::Or => {
10321050
span_bug!(span, "&& and || are not overloadable")
@@ -1062,6 +1080,9 @@ enum BinOpCategory {
10621080
/// ==, !=, etc -- takes equal types, produces bools, except for simd,
10631081
/// which produce the input type
10641082
Comparison,
1083+
1084+
ThreeWayComparison,
1085+
ThreeWayComparisonPartial,
10651086
}
10661087

10671088
impl BinOpCategory {
@@ -1086,6 +1107,9 @@ impl BinOpCategory {
10861107
| hir::BinOpKind::Ge
10871108
| hir::BinOpKind::Gt => BinOpCategory::Comparison,
10881109

1110+
hir::BinOpKind::Cmp => BinOpCategory::ThreeWayComparison,
1111+
hir::BinOpKind::CmpPartial => BinOpCategory::ThreeWayComparisonPartial,
1112+
10891113
hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
10901114
}
10911115
}
@@ -1157,7 +1181,9 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
11571181
|| lhs.is_bool() && rhs.is_bool()
11581182
}
11591183

1160-
BinOpCategory::Comparison => {
1184+
BinOpCategory::Comparison
1185+
| BinOpCategory::ThreeWayComparison
1186+
| BinOpCategory::ThreeWayComparisonPartial => {
11611187
lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
11621188
}
11631189
}

compiler/rustc_lint/src/unused.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
161161
| hir::BinOpKind::Le
162162
| hir::BinOpKind::Ne
163163
| hir::BinOpKind::Ge
164-
| hir::BinOpKind::Gt => Some("comparison"),
164+
| hir::BinOpKind::Gt
165+
| hir::BinOpKind::Cmp
166+
| hir::BinOpKind::CmpPartial => Some("comparison"),
165167
hir::BinOpKind::Add
166168
| hir::BinOpKind::Sub
167169
| hir::BinOpKind::Div

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,9 @@ fn bin_op(op: hir::BinOpKind) -> BinOp {
13281328
hir::BinOpKind::Ne => BinOp::Ne,
13291329
hir::BinOpKind::Ge => BinOp::Ge,
13301330
hir::BinOpKind::Gt => BinOp::Gt,
1331-
_ => bug!("no equivalent for ast binop {:?}", op),
1331+
hir::BinOpKind::Cmp => BinOp::Cmp,
1332+
hir::BinOpKind::And | hir::BinOpKind::Or | hir::BinOpKind::CmpPartial => {
1333+
bug!("no equivalent for ast binop {:?}", op)
1334+
}
13321335
}
13331336
}

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
11051105
check_kinds!(
11061106
x,
11071107
"Cannot three-way compare non-integer type {:?}",
1108-
ty::Char | ty::Uint(..) | ty::Int(..)
1108+
ty::Char | ty::Uint(..) | ty::Int(..) | ty::Bool
11091109
)
11101110
}
11111111
}

compiler/rustc_parse/src/errors.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,6 @@ pub(crate) enum InvalidComparisonOperatorSub {
210210
invalid: String,
211211
correct: String,
212212
},
213-
#[label(parse_spaceship_operator_invalid)]
214-
Spaceship(#[primary_span] Span),
215213
}
216214

217215
#[derive(Diagnostic)]

compiler/rustc_parse/src/parser/expr.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,19 @@ impl<'a> Parser<'a> {
154154
}
155155

156156
self.expected_token_types.insert(TokenType::Operator);
157-
while let Some(op) = self.check_assoc_op() {
157+
while let Some(mut op) = self.check_assoc_op() {
158+
// Look for `<=>`
159+
// if op.node == AssocOp::LessEqual
160+
// && self.token == token::Gt
161+
// && self.prev_token.span.hi() == self.token.span.lo()
162+
if op.node == AssocOp::LessEqual && self.look_ahead(1, |t| *t == token::Gt) {
163+
//let sp = op.span.to(self.token.span);
164+
//op = source_map::respan(sp, AssocOp::Cmp);
165+
op.node = AssocOp::Cmp;
166+
self.bump();
167+
}
168+
let op = op;
169+
158170
let lhs_span = self.interpolated_or_expr_span(&lhs);
159171
let cur_op_span = self.token.span;
160172
let restrictions = if op.node.is_assign_like() {
@@ -230,6 +242,7 @@ impl<'a> Parser<'a> {
230242
self.bump();
231243
}
232244

245+
/*
233246
// Look for C++'s `<=>` and recover
234247
if op.node == AssocOp::LessEqual
235248
&& self.token == token::Gt
@@ -243,6 +256,7 @@ impl<'a> Parser<'a> {
243256
});
244257
self.bump();
245258
}
259+
*/
246260

247261
if self.prev_token == token::BinOp(token::Plus)
248262
&& self.token == token::BinOp(token::Plus)
@@ -307,7 +321,9 @@ impl<'a> Parser<'a> {
307321
| AssocOp::LessEqual
308322
| AssocOp::NotEqual
309323
| AssocOp::Greater
310-
| AssocOp::GreaterEqual => {
324+
| AssocOp::GreaterEqual
325+
| AssocOp::Cmp
326+
| AssocOp::CmpPartial => {
311327
let ast_op = op.to_ast_binop().unwrap();
312328
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
313329
self.mk_expr(span, binary)

library/core/src/cmp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,7 @@ impl<T: Clone> Clone for Reverse<T> {
937937
/// [`PartialOrd`] and [`PartialEq`] to disagree.
938938
///
939939
/// [`cmp`]: Ord::cmp
940+
#[cfg_attr(not(bootstrap), lang = "Ord")]
940941
#[doc(alias = "<")]
941942
#[doc(alias = ">")]
942943
#[doc(alias = "<=")]

library/core/src/tuple.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,21 @@ macro_rules! lexical_partial_cmp {
197197

198198
macro_rules! lexical_cmp {
199199
($a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {
200-
match ($a).cmp(&$b) {
200+
match lexical_cmp!($a, $b) {
201201
Equal => lexical_cmp!($($rest_a, $rest_b),+),
202202
ordering => ordering
203203
}
204204
};
205-
($a:expr, $b:expr) => { ($a).cmp(&$b) };
205+
($a:expr, $b:expr) => {{
206+
#[cfg(bootstrap)]
207+
{
208+
($a).cmp(&$b)
209+
}
210+
#[cfg(not(bootstrap))]
211+
{
212+
$a <=> $b
213+
}
214+
}};
206215
}
207216

208217
macro_rules! last_type {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// MIR for `bar` after built
2+
3+
fn bar(_1: (i32, u32), _2: (i32, u32)) -> std::cmp::Ordering {
4+
debug a => _1;
5+
debug b => _2;
6+
let mut _0: std::cmp::Ordering;
7+
let mut _3: &(i32, u32);
8+
let mut _4: &(i32, u32);
9+
10+
bb0: {
11+
StorageLive(_3);
12+
_3 = &_1;
13+
StorageLive(_4);
14+
_4 = &_2;
15+
_0 = <(i32, u32) as Ord>::cmp(move _3, move _4) -> [return: bb1, unwind: bb2];
16+
}
17+
18+
bb1: {
19+
StorageDead(_4);
20+
StorageDead(_3);
21+
return;
22+
}
23+
24+
bb2 (cleanup): {
25+
resume;
26+
}
27+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// MIR for `foo` after built
2+
3+
fn foo(_1: i32, _2: i32) -> std::cmp::Ordering {
4+
debug a => _1;
5+
debug b => _2;
6+
let mut _0: std::cmp::Ordering;
7+
let mut _3: i32;
8+
let mut _4: i32;
9+
10+
bb0: {
11+
StorageLive(_3);
12+
_3 = copy _1;
13+
StorageLive(_4);
14+
_4 = copy _2;
15+
_0 = Cmp(move _3, move _4);
16+
StorageDead(_4);
17+
StorageDead(_3);
18+
return;
19+
}
20+
}

0 commit comments

Comments
 (0)