mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-29 10:13:54 +00:00
O(n*k)
code-size deriving on enums (better than previous O(n^k)
).
In the above formulas, `n` is the number of variants, and `k` is the number of self-args fed into deriving. In the particular case of interest (namely `PartialOrd` and `Ord`), `k` is always 2, so we are basically comparing `O(n)` versus `O(n^2)`. Also, the stage is set for having *all* enum deriving codes go through `build_enum_match_tuple` and getting rid of `build_enum_match`. Also, seriously attempted to clean up the code itself. Added a bunch of comments attempting to document what I learned as I worked through the original code and adapted it to this new strategy.
This commit is contained in:
parent
5d1bdc320b
commit
c8ae44682d
@ -69,7 +69,7 @@ fn cs_clone(
|
|||||||
ctor_ident = variant.node.name;
|
ctor_ident = variant.node.name;
|
||||||
all_fields = af;
|
all_fields = af;
|
||||||
},
|
},
|
||||||
EnumNonMatching(..) => {
|
EnumNonMatching(..) | EnumNonMatchingCollapsed (..) => {
|
||||||
cx.span_bug(trait_span,
|
cx.span_bug(trait_span,
|
||||||
format!("non-matching enum variants in \
|
format!("non-matching enum variants in \
|
||||||
`deriving({})`",
|
`deriving({})`",
|
||||||
|
@ -27,10 +27,12 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
|
|||||||
// any fields are not equal or if the enum variants are different
|
// any fields are not equal or if the enum variants are different
|
||||||
fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> Gc<Expr> {
|
fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> Gc<Expr> {
|
||||||
cs_and(|cx, span, _, _| cx.expr_bool(span, false),
|
cs_and(|cx, span, _, _| cx.expr_bool(span, false),
|
||||||
cx, span, substr)
|
|cx, span, _, _| cx.expr_bool(span, false),
|
||||||
|
cx, span, substr)
|
||||||
}
|
}
|
||||||
fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> Gc<Expr> {
|
fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> Gc<Expr> {
|
||||||
cs_or(|cx, span, _, _| cx.expr_bool(span, true),
|
cs_or(|cx, span, _, _| cx.expr_bool(span, true),
|
||||||
|
|cx, span, _, _| cx.expr_bool(span, true),
|
||||||
cx, span, substr)
|
cx, span, substr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
|||||||
args: vec!(borrowed_self()),
|
args: vec!(borrowed_self()),
|
||||||
ret_ty: Literal(Path::new(vec!("bool"))),
|
ret_ty: Literal(Path::new(vec!("bool"))),
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
on_nonmatching: NonMatchesExplode,
|
on_nonmatching: NonMatchesCollapseWithTags,
|
||||||
combine_substructure: combine_substructure(|cx, span, substr| {
|
combine_substructure: combine_substructure(|cx, span, substr| {
|
||||||
cs_op($op, $equal, cx, span, substr)
|
cs_op($op, $equal, cx, span, substr)
|
||||||
})
|
})
|
||||||
@ -59,7 +59,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
|||||||
args: vec![borrowed_self()],
|
args: vec![borrowed_self()],
|
||||||
ret_ty: ret_ty,
|
ret_ty: ret_ty,
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
on_nonmatching: NonMatchesExplode,
|
on_nonmatching: NonMatchesCollapseWithTags,
|
||||||
combine_substructure: combine_substructure(|cx, span, substr| {
|
combine_substructure: combine_substructure(|cx, span, substr| {
|
||||||
cs_partial_cmp(cx, span, substr)
|
cs_partial_cmp(cx, span, substr)
|
||||||
})
|
})
|
||||||
@ -96,6 +96,24 @@ pub fn some_ordering_const(cx: &mut ExtCtxt, span: Span, cnst: Ordering) -> Gc<a
|
|||||||
cx.expr_some(span, ordering)
|
cx.expr_some(span, ordering)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum OrderingOp {
|
||||||
|
PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
|
||||||
|
span: Span,
|
||||||
|
op: OrderingOp,
|
||||||
|
self_arg_tags: &[ast::Ident]) -> Gc<ast::Expr> {
|
||||||
|
let lft = cx.expr_ident(span, self_arg_tags[0]);
|
||||||
|
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
|
||||||
|
let op_str = match op {
|
||||||
|
PartialCmpOp => "partial_cmp",
|
||||||
|
LtOp => "lt", LeOp => "le",
|
||||||
|
GtOp => "gt", GeOp => "ge",
|
||||||
|
};
|
||||||
|
cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
|
pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
|
||||||
substr: &Substructure) -> Gc<Expr> {
|
substr: &Substructure) -> Gc<Expr> {
|
||||||
let test_id = cx.ident_of("__test");
|
let test_id = cx.ident_of("__test");
|
||||||
@ -147,7 +165,14 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
|
|||||||
// later one.
|
// later one.
|
||||||
[(self_var, _, _), (other_var, _, _)] =>
|
[(self_var, _, _), (other_var, _, _)] =>
|
||||||
some_ordering_const(cx, span, self_var.cmp(&other_var)),
|
some_ordering_const(cx, span, self_var.cmp(&other_var)),
|
||||||
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`"),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||||
|
if self_args.len() != 2 {
|
||||||
|
cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
||||||
|
} else {
|
||||||
|
some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cx, span, substr)
|
cx, span, substr)
|
||||||
@ -206,5 +231,16 @@ fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt, span: Span,
|
|||||||
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||||
|
if self_args.len() != 2 {
|
||||||
|
cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
||||||
|
} else {
|
||||||
|
let op = match (less, equal) {
|
||||||
|
(true, true) => LeOp, (true, false) => LtOp,
|
||||||
|
(false, true) => GeOp, (false, false) => GtOp,
|
||||||
|
};
|
||||||
|
some_ordering_collapsed(cx, span, op, tag_tuple)
|
||||||
|
}
|
||||||
|
},
|
||||||
cx, span, substr)
|
cx, span, substr)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ pub fn expand_deriving_totaleq(cx: &mut ExtCtxt,
|
|||||||
let block = cx.block(span, stmts, None);
|
let block = cx.block(span, stmts, None);
|
||||||
cx.expr_block(block)
|
cx.expr_block(block)
|
||||||
},
|
},
|
||||||
|
|cx, sp, _, _| cx.span_bug(sp, "non matching enums in deriving(Eq)?"),
|
||||||
|cx, sp, _, _| cx.span_bug(sp, "non matching enums in deriving(Eq)?"),
|
|cx, sp, _, _| cx.span_bug(sp, "non matching enums in deriving(Eq)?"),
|
||||||
cx,
|
cx,
|
||||||
span,
|
span,
|
||||||
|
@ -41,7 +41,7 @@ pub fn expand_deriving_totalord(cx: &mut ExtCtxt,
|
|||||||
args: vec!(borrowed_self()),
|
args: vec!(borrowed_self()),
|
||||||
ret_ty: Literal(Path::new(vec!("std", "cmp", "Ordering"))),
|
ret_ty: Literal(Path::new(vec!("std", "cmp", "Ordering"))),
|
||||||
attributes: attrs,
|
attributes: attrs,
|
||||||
on_nonmatching: NonMatchesExplode,
|
on_nonmatching: NonMatchesCollapseWithTags,
|
||||||
combine_substructure: combine_substructure(|a, b, c| {
|
combine_substructure: combine_substructure(|a, b, c| {
|
||||||
cs_cmp(a, b, c)
|
cs_cmp(a, b, c)
|
||||||
}),
|
}),
|
||||||
@ -65,6 +65,14 @@ pub fn ordering_const(cx: &mut ExtCtxt, span: Span, cnst: Ordering) -> ast::Path
|
|||||||
cx.ident_of(cnst)))
|
cx.ident_of(cnst)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ordering_collapsed(cx: &mut ExtCtxt,
|
||||||
|
span: Span,
|
||||||
|
self_arg_tags: &[ast::Ident]) -> Gc<ast::Expr> {
|
||||||
|
let lft = cx.expr_ident(span, self_arg_tags[0]);
|
||||||
|
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
|
||||||
|
cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
|
pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
|
||||||
substr: &Substructure) -> Gc<Expr> {
|
substr: &Substructure) -> Gc<Expr> {
|
||||||
let test_id = cx.ident_of("__test");
|
let test_id = cx.ident_of("__test");
|
||||||
@ -122,5 +130,12 @@ pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
|
|||||||
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
_ => cx.span_bug(span, "not exactly 2 arguments in `deriving(Ord)`")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||||
|
if self_args.len() != 2 {
|
||||||
|
cx.span_bug(span, "not exactly 2 arguments in `deriving(TotalOrd)`")
|
||||||
|
} else {
|
||||||
|
ordering_collapsed(cx, span, tag_tuple)
|
||||||
|
}
|
||||||
|
},
|
||||||
cx, span, substr)
|
cx, span, substr)
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@
|
|||||||
//! }])
|
//! }])
|
||||||
//! ~~~
|
//! ~~~
|
||||||
//!
|
//!
|
||||||
//! For `C1 {x}` and `C1 {x}` ,
|
//! For `C1 {x}` and `C1 {x}`,
|
||||||
//!
|
//!
|
||||||
//! ~~~text
|
//! ~~~text
|
||||||
//! EnumMatching(1, <ast::Variant for C1>,
|
//! EnumMatching(1, <ast::Variant for C1>,
|
||||||
@ -150,14 +150,20 @@
|
|||||||
//! For `C0(a)` and `C1 {x}` ,
|
//! For `C0(a)` and `C1 {x}` ,
|
||||||
//!
|
//!
|
||||||
//! ~~~text
|
//! ~~~text
|
||||||
//! EnumNonMatching(~[(0, <ast::Variant for B0>,
|
//! EnumNonMatchingCollapsed(
|
||||||
//! ~[(<span of int>, None, <expr for &a>)]),
|
//! ~[<ident of self>, <ident of __arg_1>],
|
||||||
//! (1, <ast::Variant for B1>,
|
//! &[<ast::Variant for C0>, <ast::Variant for C1>],
|
||||||
//! ~[(<span of x>, Some(<ident of x>),
|
//! &[<ident for self index value>, <ident of __arg_1 index value>])
|
||||||
//! <expr for &other.x>)])])
|
|
||||||
//! ~~~
|
//! ~~~
|
||||||
//!
|
//!
|
||||||
//! (and vice versa, but with the order of the outermost list flipped.)
|
//! It is the same for when the arguments are flipped to `C1 {x}` and
|
||||||
|
//! `C0(a)`; the only difference is what the values of the identifiers
|
||||||
|
//! <ident for self index value> and <ident of __arg_1 index value> will
|
||||||
|
//! be in the generated code.
|
||||||
|
//!
|
||||||
|
//! `EnumNonMatchingCollapsed` deliberately provides far less information
|
||||||
|
//! than is generally available for a given pair of variants; see #15375
|
||||||
|
//! for discussion.
|
||||||
//!
|
//!
|
||||||
//! ## Static
|
//! ## Static
|
||||||
//!
|
//!
|
||||||
@ -172,7 +178,6 @@
|
|||||||
//! (<ident of C1>, <span of C1>,
|
//! (<ident of C1>, <span of C1>,
|
||||||
//! Named(~[(<ident of x>, <span of x>)]))])
|
//! Named(~[(<ident of x>, <span of x>)]))])
|
||||||
//! ~~~
|
//! ~~~
|
||||||
//!
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::gc::{Gc, GC};
|
use std::gc::{Gc, GC};
|
||||||
@ -215,9 +220,18 @@ pub struct TraitDef<'a> {
|
|||||||
|
|
||||||
#[deriving(PartialEq, Eq)]
|
#[deriving(PartialEq, Eq)]
|
||||||
pub enum HandleNonMatchingEnums {
|
pub enum HandleNonMatchingEnums {
|
||||||
NonMatchesCollapse, // handle all non-matches via one `_ => ..` clause
|
/// handle all non-matches via one `_ => ..` clause
|
||||||
NonMatchesExplode, // handle via n^k cases for n variants and k self-args
|
NonMatchesCollapse,
|
||||||
NonMatchHandlingIrrelevant, // cannot encounter two enums of Self type
|
|
||||||
|
/// handle all non-matches via one `_ => ..` clause that has
|
||||||
|
/// access to a tuple of tags indicating each variant index.
|
||||||
|
NonMatchesCollapseWithTags,
|
||||||
|
|
||||||
|
/// handle via n^k cases for n variants and k self-args
|
||||||
|
NonMatchesExplode,
|
||||||
|
|
||||||
|
/// cannot encounter two enums of Self type
|
||||||
|
NonMatchHandlingIrrelevant,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MethodDef<'a> {
|
pub struct MethodDef<'a> {
|
||||||
@ -308,6 +322,16 @@ pub enum SubstructureFields<'a> {
|
|||||||
EnumNonMatching(&'a [(uint, P<ast::Variant>,
|
EnumNonMatching(&'a [(uint, P<ast::Variant>,
|
||||||
Vec<(Span, Option<Ident>, Gc<Expr>)>)]),
|
Vec<(Span, Option<Ident>, Gc<Expr>)>)]),
|
||||||
|
|
||||||
|
/**
|
||||||
|
non-matching variants of the enum, but with all state hidden from
|
||||||
|
the consequent code. The first component holds Idents for all of
|
||||||
|
the Self arguments; the second component is a slice of all of the
|
||||||
|
variants for the enum itself, and the third component is a list of
|
||||||
|
Idents bound to the variant index values for each of the actual
|
||||||
|
input Self arguments.
|
||||||
|
*/
|
||||||
|
EnumNonMatchingCollapsed(Vec<Ident>, &'a [Gc<ast::Variant>], &'a [Ident]),
|
||||||
|
|
||||||
/// A static method where Self is a struct.
|
/// A static method where Self is a struct.
|
||||||
StaticStruct(&'a ast::StructDef, StaticFields),
|
StaticStruct(&'a ast::StructDef, StaticFields),
|
||||||
/// A static method where Self is an enum.
|
/// A static method where Self is an enum.
|
||||||
@ -324,14 +348,16 @@ pub type CombineSubstructureFunc<'a> =
|
|||||||
|&mut ExtCtxt, Span, &Substructure|: 'a -> Gc<Expr>;
|
|&mut ExtCtxt, Span, &Substructure|: 'a -> Gc<Expr>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Deal with non-matching enum variants, the arguments are a list
|
Deal with non-matching enum variants. The tuple is a list of
|
||||||
representing each variant: (variant index, ast::Variant instance,
|
identifiers (one for each Self argument, which could be any of the
|
||||||
[variant fields]), and a list of the nonself args of the type
|
variants since they have been collapsed together) and the identifiers
|
||||||
|
holding the variant index value for each of the Self arguments. The
|
||||||
|
last argument is all the non-Self args of the method being derived.
|
||||||
*/
|
*/
|
||||||
pub type EnumNonMatchFunc<'a> =
|
pub type EnumNonMatchCollapsedFunc<'a> =
|
||||||
|&mut ExtCtxt,
|
|&mut ExtCtxt,
|
||||||
Span,
|
Span,
|
||||||
&[(uint, P<ast::Variant>, Vec<(Span, Option<Ident>, Gc<Expr>)>)],
|
(&[Ident], &[Ident]),
|
||||||
&[Gc<Expr>]|: 'a
|
&[Gc<Expr>]|: 'a
|
||||||
-> Gc<Expr>;
|
-> Gc<Expr>;
|
||||||
|
|
||||||
@ -531,6 +557,15 @@ impl<'a> TraitDef<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn variant_to_pat(cx: &mut ExtCtxt, sp: Span, variant: &ast::Variant)
|
||||||
|
-> Gc<ast::Pat> {
|
||||||
|
let ident = cx.path_ident(sp, variant.node.name);
|
||||||
|
cx.pat(sp, match variant.node.kind {
|
||||||
|
ast::TupleVariantKind(..) => ast::PatEnum(ident, None),
|
||||||
|
ast::StructVariantKind(..) => ast::PatStruct(ident, Vec::new(), true),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> MethodDef<'a> {
|
impl<'a> MethodDef<'a> {
|
||||||
fn call_substructure_method(&self,
|
fn call_substructure_method(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
@ -769,27 +804,32 @@ impl<'a> MethodDef<'a> {
|
|||||||
~~~
|
~~~
|
||||||
#[deriving(PartialEq)]
|
#[deriving(PartialEq)]
|
||||||
enum A {
|
enum A {
|
||||||
A1
|
A1,
|
||||||
A2(int)
|
A2(int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// is equivalent to (with on_nonmatching == NonMatchesExplode)
|
// is equivalent to
|
||||||
|
|
||||||
impl PartialEq for A {
|
impl PartialEq for A {
|
||||||
fn eq(&self, __arg_1: &A) {
|
fn eq(&self, __arg_1: &A) -> ::bool {
|
||||||
match *self {
|
match (&*self, &*__arg_1) {
|
||||||
A1 => match *__arg_1 {
|
(&A1, &A1) => true,
|
||||||
A1 => true
|
(&A2(ref __self_0),
|
||||||
A2(ref __arg_1_1) => false
|
&A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)),
|
||||||
},
|
_ => {
|
||||||
A2(self_1) => match *__arg_1 {
|
let __self_vi = match *self { A1(..) => 0u, A2(..) => 1u };
|
||||||
A1 => false,
|
let __arg_1_vi = match *__arg_1 { A1(..) => 0u, A2(..) => 1u };
|
||||||
A2(ref __arg_1_1) => self_1.eq(__arg_1_1)
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
|
(Of course `__self_vi` and `__arg_1_vi` are unused for
|
||||||
|
`PartialEq`, and those subcomputations will hopefully be removed
|
||||||
|
as their results are unused. The point of `__self_vi` and
|
||||||
|
`__arg_1_vi` is for `PartialOrd`; see #15503.)
|
||||||
*/
|
*/
|
||||||
fn expand_enum_method_body(&self,
|
fn expand_enum_method_body(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
@ -800,9 +840,15 @@ impl<'a> MethodDef<'a> {
|
|||||||
nonself_args: &[Gc<Expr>])
|
nonself_args: &[Gc<Expr>])
|
||||||
-> Gc<Expr> {
|
-> Gc<Expr> {
|
||||||
let mut matches = Vec::new();
|
let mut matches = Vec::new();
|
||||||
self.build_enum_match(cx, trait_, enum_def, type_ident,
|
match self.on_nonmatching {
|
||||||
self_args, nonself_args,
|
NonMatchesCollapseWithTags =>
|
||||||
None, &mut matches, 0)
|
self.build_enum_match_tuple(
|
||||||
|
cx, trait_, enum_def, type_ident, self_args, nonself_args),
|
||||||
|
NonMatchesCollapse | NonMatchesExplode | NonMatchHandlingIrrelevant =>
|
||||||
|
self.build_enum_match(
|
||||||
|
cx, trait_, enum_def, type_ident, self_args, nonself_args,
|
||||||
|
None, &mut matches, 0),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -906,6 +952,10 @@ impl<'a> MethodDef<'a> {
|
|||||||
|
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
|
|
||||||
|
assert!(self.on_nonmatching == NonMatchesCollapse ||
|
||||||
|
self.on_nonmatching == NonMatchesExplode ||
|
||||||
|
self.on_nonmatching == NonMatchHandlingIrrelevant);
|
||||||
|
|
||||||
// the code for nonmatching variants only matters when
|
// the code for nonmatching variants only matters when
|
||||||
// we've seen at least one other variant already
|
// we've seen at least one other variant already
|
||||||
assert!(match_count == 0 ||
|
assert!(match_count == 0 ||
|
||||||
@ -987,6 +1037,293 @@ impl<'a> MethodDef<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates a match for a tuple of all `self_args`, where either all
|
||||||
|
variants match, or it falls into a catch-all for when one variant
|
||||||
|
does not match.
|
||||||
|
|
||||||
|
There are N + 1 cases because is a case for each of the N
|
||||||
|
variants where all of the variants match, and one catch-all for
|
||||||
|
when one does not match.
|
||||||
|
|
||||||
|
The catch-all handler is provided access the variant index values
|
||||||
|
for each of the self-args, carried in precomputed variables. (Nota
|
||||||
|
bene: the variant index values are not necessarily the
|
||||||
|
discriminant values. See issue #15523.)
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
match (this, that, ...) {
|
||||||
|
(Variant1, Variant1, Variant1) => ... // delegate Matching on Variant1
|
||||||
|
(Variant2, Variant2, Variant2) => ... // delegate Matching on Variant2
|
||||||
|
...
|
||||||
|
_ => {
|
||||||
|
let __this_vi = match this { Variant1 => 0u, Variant2 => 1u, ... };
|
||||||
|
let __that_vi = match that { Variant1 => 0u, Variant2 => 1u, ... };
|
||||||
|
... // catch-all remainder can inspect above variant index values.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
*/
|
||||||
|
fn build_enum_match_tuple(
|
||||||
|
&self,
|
||||||
|
cx: &mut ExtCtxt,
|
||||||
|
trait_: &TraitDef,
|
||||||
|
enum_def: &EnumDef,
|
||||||
|
type_ident: Ident,
|
||||||
|
self_args: &[Gc<Expr>],
|
||||||
|
nonself_args: &[Gc<Expr>]) -> Gc<Expr> {
|
||||||
|
|
||||||
|
let sp = trait_.span;
|
||||||
|
let variants = &enum_def.variants;
|
||||||
|
|
||||||
|
let self_arg_names = self_args.iter().enumerate()
|
||||||
|
.map(|(arg_count, _self_arg)| {
|
||||||
|
if arg_count == 0 {
|
||||||
|
"__self".to_string()
|
||||||
|
} else {
|
||||||
|
format!("__arg_{}", arg_count)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
let self_arg_idents = self_arg_names.iter()
|
||||||
|
.map(|name|cx.ident_of(name.as_slice()))
|
||||||
|
.collect::<Vec<ast::Ident>>();
|
||||||
|
|
||||||
|
// The `vi_idents` will be bound, solely in the catch-all, to
|
||||||
|
// a series of let statements mapping each self_arg to a uint
|
||||||
|
// corresponding to its variant index.
|
||||||
|
let vi_idents : Vec<ast::Ident> = self_arg_names.iter()
|
||||||
|
.map(|name| { let vi_suffix = format!("{:s}_vi", name.as_slice());
|
||||||
|
cx.ident_of(vi_suffix.as_slice()) })
|
||||||
|
.collect::<Vec<ast::Ident>>();
|
||||||
|
|
||||||
|
// Builds, via callback to call_substructure_method, the
|
||||||
|
// delegated expression that handles the catch-all case,
|
||||||
|
// using `__variants_tuple` to drive logic if necessary.
|
||||||
|
let catch_all_substructure = EnumNonMatchingCollapsed(
|
||||||
|
self_arg_idents, variants.as_slice(), vi_idents.as_slice());
|
||||||
|
|
||||||
|
// These arms are of the form:
|
||||||
|
// (Variant1, Variant1, ...) => Body1
|
||||||
|
// (Variant2, Variant2, ...) => Body2
|
||||||
|
// ...
|
||||||
|
// where each tuple has length = self_args.len()
|
||||||
|
let mut match_arms : Vec<ast::Arm> = variants.iter().enumerate()
|
||||||
|
.map(|(index, &variant)| {
|
||||||
|
|
||||||
|
// These self_pats have form Variant1, Variant2, ...
|
||||||
|
let self_pats : Vec<(Gc<ast::Pat>,
|
||||||
|
Vec<(Span, Option<Ident>, Gc<Expr>)>)>;
|
||||||
|
self_pats = self_arg_names.iter()
|
||||||
|
.map(|self_arg_name|
|
||||||
|
trait_.create_enum_variant_pattern(
|
||||||
|
cx, &*variant, self_arg_name.as_slice(),
|
||||||
|
ast::MutImmutable))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
|
||||||
|
// (see "Final wrinkle" note below for why.)
|
||||||
|
let subpats = self_pats.iter()
|
||||||
|
.map(|&(p, ref _idents)| cx.pat(sp, ast::PatRegion(p)))
|
||||||
|
.collect::<Vec<Gc<ast::Pat>>>();
|
||||||
|
|
||||||
|
// Here is the pat = `(&VariantK, &VariantK, ...)`
|
||||||
|
let single_pat = cx.pat(sp, ast::PatTup(subpats));
|
||||||
|
|
||||||
|
// For the BodyK, we need to delegate to our caller,
|
||||||
|
// passing it an EnumMatching to indicate which case
|
||||||
|
// we are in.
|
||||||
|
|
||||||
|
// All of the Self args have the same variant in these
|
||||||
|
// cases. So we transpose the info in self_pats to
|
||||||
|
// gather the getter expressions together, in the form
|
||||||
|
// that EnumMatching expects.
|
||||||
|
|
||||||
|
// The transposition is driven by walking across the
|
||||||
|
// arg fields of the variant for the first self pat.
|
||||||
|
let &(_, ref self_arg_fields) = self_pats.get(0);
|
||||||
|
|
||||||
|
let field_tuples : Vec<FieldInfo>;
|
||||||
|
|
||||||
|
field_tuples = self_arg_fields.iter().enumerate()
|
||||||
|
// For each arg field of self, pull out its getter expr ...
|
||||||
|
.map(|(field_index, &(sp, opt_ident, self_getter_expr))| {
|
||||||
|
// ... but FieldInfo also wants getter expr
|
||||||
|
// for matching other arguments of Self type;
|
||||||
|
// so walk across the *other* self_pats and
|
||||||
|
// pull out getter for same field in each of
|
||||||
|
// them (using `field_index` tracked above).
|
||||||
|
// That is the heart of the transposition.
|
||||||
|
let others = self_pats.tail().iter()
|
||||||
|
.map(|&(_pat, ref fields)| {
|
||||||
|
|
||||||
|
let &(_, _opt_ident, other_getter_expr) =
|
||||||
|
fields.get(field_index);
|
||||||
|
|
||||||
|
// All Self args have same variant, so
|
||||||
|
// opt_idents are the same. (Assert
|
||||||
|
// here to make it self-evident that
|
||||||
|
// it is okay to ignore `_opt_ident`.)
|
||||||
|
assert!(opt_ident == _opt_ident);
|
||||||
|
|
||||||
|
other_getter_expr
|
||||||
|
}).collect::<Vec<Gc<Expr>>>();
|
||||||
|
|
||||||
|
FieldInfo { span: sp,
|
||||||
|
name: opt_ident,
|
||||||
|
self_: self_getter_expr,
|
||||||
|
other: others,
|
||||||
|
}
|
||||||
|
}).collect::<Vec<FieldInfo>>();
|
||||||
|
|
||||||
|
// Now, for some given VariantK, we have built up
|
||||||
|
// expressions for referencing every field of every
|
||||||
|
// Self arg, assuming all are instances of VariantK.
|
||||||
|
// Build up code associated with such a case.
|
||||||
|
let substructure = EnumMatching(index, variant, field_tuples);
|
||||||
|
let arm_expr = self.call_substructure_method(
|
||||||
|
cx, trait_, type_ident, self_args, nonself_args,
|
||||||
|
&substructure);
|
||||||
|
|
||||||
|
cx.arm(sp, vec![single_pat], arm_expr)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
// We will usually need the catch-all after matching the
|
||||||
|
// tuples `(VariantK, VariantK, ...)` for each VariantK of the
|
||||||
|
// enum. But:
|
||||||
|
//
|
||||||
|
// * when there is only one Self arg, the arms above suffice
|
||||||
|
// (and the deriving we call back into may not be prepared to
|
||||||
|
// handle EnumNonMatchCollapsed), and,
|
||||||
|
//
|
||||||
|
// * when the enum has only one variant, the single arm that
|
||||||
|
// is already present always suffices.
|
||||||
|
//
|
||||||
|
// * In either of the two cases above, if we *did* add a
|
||||||
|
// catch-all `_` match, it would trigger the
|
||||||
|
// unreachable-pattern error.
|
||||||
|
//
|
||||||
|
if variants.len() > 1 && self_args.len() > 1 {
|
||||||
|
let arms : Vec<ast::Arm> = variants.iter().enumerate()
|
||||||
|
.map(|(index, &variant)| {
|
||||||
|
let pat = variant_to_pat(cx, sp, &*variant);
|
||||||
|
let lit = ast::LitUint(index as u64, ast::TyU);
|
||||||
|
cx.arm(sp, vec![pat], cx.expr_lit(sp, lit))
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
// Build a series of let statements mapping each self_arg
|
||||||
|
// to a uint corresponding to its variant index.
|
||||||
|
// i.e. for `enum E<T> { A, B(1), C(T, T) }`, and a deriving
|
||||||
|
// with three Self args, builds three statements:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// let __self0_vi = match self {
|
||||||
|
// A => 0u, B(..) => 1u, C(..) => 2u
|
||||||
|
// };
|
||||||
|
// let __self1_vi = match __arg1 {
|
||||||
|
// A => 0u, B(..) => 1u, C(..) => 2u
|
||||||
|
// };
|
||||||
|
// let __self2_vi = match __arg2 {
|
||||||
|
// A => 0u, B(..) => 1u, C(..) => 2u
|
||||||
|
// };
|
||||||
|
// ```
|
||||||
|
let mut index_let_stmts : Vec<Gc<ast::Stmt>> = Vec::new();
|
||||||
|
for (&ident, &self_arg) in vi_idents.iter().zip(self_args.iter()) {
|
||||||
|
let variant_idx = cx.expr_match(sp, self_arg, arms.clone());
|
||||||
|
let let_stmt = cx.stmt_let(sp, false, ident, variant_idx);
|
||||||
|
index_let_stmts.push(let_stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
let arm_expr = self.call_substructure_method(
|
||||||
|
cx, trait_, type_ident, self_args, nonself_args,
|
||||||
|
&catch_all_substructure);
|
||||||
|
|
||||||
|
// Builds the expression:
|
||||||
|
// {
|
||||||
|
// let __self0_vi = ...;
|
||||||
|
// let __self1_vi = ...;
|
||||||
|
// ...
|
||||||
|
// <delegated expression referring to __self0_vi, et al.>
|
||||||
|
// }
|
||||||
|
let arm_expr = cx.expr_block(
|
||||||
|
cx.block_all(sp, Vec::new(), index_let_stmts, Some(arm_expr)));
|
||||||
|
|
||||||
|
// Builds arm:
|
||||||
|
// _ => { let __self0_vi = ...;
|
||||||
|
// let __self1_vi = ...;
|
||||||
|
// ...
|
||||||
|
// <delegated expression as above> }
|
||||||
|
let catch_all_match_arm =
|
||||||
|
cx.arm(sp, vec![cx.pat_wild(sp)], arm_expr);
|
||||||
|
|
||||||
|
match_arms.push(catch_all_match_arm);
|
||||||
|
|
||||||
|
} else if variants.len() == 0 {
|
||||||
|
// As an additional wrinkle, For a zero-variant enum A,
|
||||||
|
// currently the compiler
|
||||||
|
// will accept `fn (a: &Self) { match *a { } }`
|
||||||
|
// but rejects `fn (a: &Self) { match (&*a,) { } }`
|
||||||
|
// as well as `fn (a: &Self) { match ( *a,) { } }`
|
||||||
|
//
|
||||||
|
// This means that the strategy of building up a tuple of
|
||||||
|
// all Self arguments fails when Self is a zero variant
|
||||||
|
// enum: rustc rejects the expanded program, even though
|
||||||
|
// the actual code tends to be impossible to execute (at
|
||||||
|
// least safely), according to the type system.
|
||||||
|
//
|
||||||
|
// The most expedient fix for this is to just let the
|
||||||
|
// code fall through to the catch-all. But even this is
|
||||||
|
// error-prone, since the catch-all as defined above would
|
||||||
|
// generate code like this:
|
||||||
|
//
|
||||||
|
// _ => { let __self0 = match *self { };
|
||||||
|
// let __self1 = match *__arg_0 { };
|
||||||
|
// <catch-all-expr> }
|
||||||
|
//
|
||||||
|
// Which is yields bindings for variables which type
|
||||||
|
// inference cannot resolve to unique types.
|
||||||
|
//
|
||||||
|
// One option to the above might be to add explicit type
|
||||||
|
// annotations. But the *only* reason to go down that path
|
||||||
|
// would be to try to make the expanded output consistent
|
||||||
|
// with the case when the number of enum variants >= 1.
|
||||||
|
//
|
||||||
|
// That just isn't worth it. In fact, trying to generate
|
||||||
|
// sensible code for *any* deriving on a zero-variant enum
|
||||||
|
// does not make sense. But at the same time, for now, we
|
||||||
|
// do not want to cause a compile failure just because the
|
||||||
|
// user happened to attach a deriving to their
|
||||||
|
// zero-variant enum.
|
||||||
|
//
|
||||||
|
// Instead, just generate a failing expression for the
|
||||||
|
// zero variant case, skipping matches and also skipping
|
||||||
|
// delegating back to the end user code entirely.
|
||||||
|
//
|
||||||
|
// (See also #4499 and #12609; note that some of the
|
||||||
|
// discussions there influence what choice we make here;
|
||||||
|
// e.g. if we feature-gate `match x { ... }` when x refers
|
||||||
|
// to an uninhabited type (e.g. a zero-variant enum or a
|
||||||
|
// type holding such an enum), but do not feature-gate
|
||||||
|
// zero-variant enums themselves, then attempting to
|
||||||
|
// derive Show on such a type could here generate code
|
||||||
|
// that needs the feature gate enabled.)
|
||||||
|
|
||||||
|
return cx.expr_unreachable(sp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final wrinkle: the self_args are expressions that deref
|
||||||
|
// down to desired l-values, but we cannot actually deref
|
||||||
|
// them when they are fed as r-values into a tuple
|
||||||
|
// expression; here add a layer of borrowing, turning
|
||||||
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
|
let borrowed_self_args = self_args.iter()
|
||||||
|
.map(|&self_arg| cx.expr_addr_of(sp, self_arg))
|
||||||
|
.collect::<Vec<Gc<ast::Expr>>>();
|
||||||
|
let match_arg = cx.expr(sp, ast::ExprTup(borrowed_self_args));
|
||||||
|
cx.expr_match(sp, match_arg, match_arms)
|
||||||
|
}
|
||||||
|
|
||||||
fn expand_static_enum_method_body(&self,
|
fn expand_static_enum_method_body(&self,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
trait_: &TraitDef,
|
trait_: &TraitDef,
|
||||||
@ -1186,6 +1523,7 @@ pub fn cs_fold(use_foldl: bool,
|
|||||||
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>, &[Gc<Expr>]| -> Gc<Expr>,
|
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>, &[Gc<Expr>]| -> Gc<Expr>,
|
||||||
base: Gc<Expr>,
|
base: Gc<Expr>,
|
||||||
enum_nonmatch_f: EnumNonMatchFunc,
|
enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
substructure: &Substructure)
|
substructure: &Substructure)
|
||||||
@ -1210,9 +1548,12 @@ pub fn cs_fold(use_foldl: bool,
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span,
|
EnumNonMatching(ref all_enums) =>
|
||||||
*all_enums,
|
enum_nonmatch_f(cx, trait_span, *all_enums,
|
||||||
substructure.nonself_args),
|
substructure.nonself_args),
|
||||||
|
EnumNonMatchingCollapsed(ref all_args, _, tuple) =>
|
||||||
|
enum_nonmatch_g(cx, trait_span, (all_args.as_slice(), tuple),
|
||||||
|
substructure.nonself_args),
|
||||||
StaticEnum(..) | StaticStruct(..) => {
|
StaticEnum(..) | StaticStruct(..) => {
|
||||||
cx.span_bug(trait_span, "static function in `deriving`")
|
cx.span_bug(trait_span, "static function in `deriving`")
|
||||||
}
|
}
|
||||||
@ -1232,6 +1573,7 @@ f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn cs_same_method(f: |&mut ExtCtxt, Span, Vec<Gc<Expr>>| -> Gc<Expr>,
|
pub fn cs_same_method(f: |&mut ExtCtxt, Span, Vec<Gc<Expr>>| -> Gc<Expr>,
|
||||||
enum_nonmatch_f: EnumNonMatchFunc,
|
enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
substructure: &Substructure)
|
substructure: &Substructure)
|
||||||
@ -1250,9 +1592,12 @@ pub fn cs_same_method(f: |&mut ExtCtxt, Span, Vec<Gc<Expr>>| -> Gc<Expr>,
|
|||||||
|
|
||||||
f(cx, trait_span, called)
|
f(cx, trait_span, called)
|
||||||
},
|
},
|
||||||
EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span,
|
EnumNonMatching(ref all_enums) =>
|
||||||
*all_enums,
|
enum_nonmatch_f(cx, trait_span, *all_enums,
|
||||||
substructure.nonself_args),
|
substructure.nonself_args),
|
||||||
|
EnumNonMatchingCollapsed(ref all_self_args, _, tuple) =>
|
||||||
|
enum_nonmatch_g(cx, trait_span, (all_self_args.as_slice(), tuple),
|
||||||
|
substructure.nonself_args),
|
||||||
StaticEnum(..) | StaticStruct(..) => {
|
StaticEnum(..) | StaticStruct(..) => {
|
||||||
cx.span_bug(trait_span, "static function in `deriving`")
|
cx.span_bug(trait_span, "static function in `deriving`")
|
||||||
}
|
}
|
||||||
@ -1269,6 +1614,7 @@ pub fn cs_same_method_fold(use_foldl: bool,
|
|||||||
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>| -> Gc<Expr>,
|
f: |&mut ExtCtxt, Span, Gc<Expr>, Gc<Expr>| -> Gc<Expr>,
|
||||||
base: Gc<Expr>,
|
base: Gc<Expr>,
|
||||||
enum_nonmatch_f: EnumNonMatchFunc,
|
enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
trait_span: Span,
|
trait_span: Span,
|
||||||
substructure: &Substructure)
|
substructure: &Substructure)
|
||||||
@ -1286,6 +1632,7 @@ pub fn cs_same_method_fold(use_foldl: bool,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
enum_nonmatch_f,
|
enum_nonmatch_f,
|
||||||
|
enum_nonmatch_g,
|
||||||
cx, trait_span, substructure)
|
cx, trait_span, substructure)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1296,6 +1643,7 @@ on all the fields.
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>,
|
pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>,
|
||||||
enum_nonmatch_f: EnumNonMatchFunc,
|
enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt, trait_span: Span,
|
cx: &mut ExtCtxt, trait_span: Span,
|
||||||
substructure: &Substructure) -> Gc<Expr> {
|
substructure: &Substructure) -> Gc<Expr> {
|
||||||
cs_same_method_fold(
|
cs_same_method_fold(
|
||||||
@ -1308,25 +1656,30 @@ pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>,
|
|||||||
},
|
},
|
||||||
base,
|
base,
|
||||||
enum_nonmatch_f,
|
enum_nonmatch_f,
|
||||||
|
enum_nonmatch_g,
|
||||||
cx, trait_span, substructure)
|
cx, trait_span, substructure)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// cs_binop with binop == or
|
/// cs_binop with binop == or
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc,
|
pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt, span: Span,
|
cx: &mut ExtCtxt, span: Span,
|
||||||
substructure: &Substructure) -> Gc<Expr> {
|
substructure: &Substructure) -> Gc<Expr> {
|
||||||
cs_binop(ast::BiOr, cx.expr_bool(span, false),
|
cs_binop(ast::BiOr, cx.expr_bool(span, false),
|
||||||
enum_nonmatch_f,
|
enum_nonmatch_f,
|
||||||
|
enum_nonmatch_g,
|
||||||
cx, span, substructure)
|
cx, span, substructure)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// cs_binop with binop == and
|
/// cs_binop with binop == and
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc,
|
pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc,
|
||||||
|
enum_nonmatch_g: EnumNonMatchCollapsedFunc,
|
||||||
cx: &mut ExtCtxt, span: Span,
|
cx: &mut ExtCtxt, span: Span,
|
||||||
substructure: &Substructure) -> Gc<Expr> {
|
substructure: &Substructure) -> Gc<Expr> {
|
||||||
cs_binop(ast::BiAnd, cx.expr_bool(span, true),
|
cs_binop(ast::BiAnd, cx.expr_bool(span, true),
|
||||||
enum_nonmatch_f,
|
enum_nonmatch_f,
|
||||||
|
enum_nonmatch_g,
|
||||||
cx, span, substructure)
|
cx, span, substructure)
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,8 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span,
|
|||||||
Struct(_) => substr.type_ident,
|
Struct(_) => substr.type_ident,
|
||||||
EnumMatching(_, v, _) => v.node.name,
|
EnumMatching(_, v, _) => v.node.name,
|
||||||
|
|
||||||
EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => {
|
EnumNonMatching(..) | EnumNonMatchingCollapsed(..) |
|
||||||
|
StaticStruct(..) | StaticEnum(..) => {
|
||||||
cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
|
cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,10 @@ pub fn expand_deriving_zero(cx: &mut ExtCtxt,
|
|||||||
"Non-matching enum \
|
"Non-matching enum \
|
||||||
variant in \
|
variant in \
|
||||||
deriving(Zero)"),
|
deriving(Zero)"),
|
||||||
|
|cx, span, _, _| cx.span_bug(span,
|
||||||
|
"Non-matching enum \
|
||||||
|
variant in \
|
||||||
|
deriving(Zero)"),
|
||||||
cx, span, substr)
|
cx, span, substr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user