diff options
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/deriving/clone.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/cmp/eq.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/cmp/ord.rs | 42 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/cmp/totaleq.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/cmp/totalord.rs | 17 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/generic/mod.rs | 425 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/show.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/ext/deriving/zero.rs | 4 |
8 files changed, 455 insertions, 43 deletions
diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 1a296906cc2..3164aaf98f2 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -69,7 +69,7 @@ fn cs_clone( ctor_ident = variant.node.name; all_fields = af; }, - EnumNonMatching(..) => { + EnumNonMatching(..) | EnumNonMatchingCollapsed (..) => { cx.span_bug(trait_span, format!("non-matching enum variants in \ `deriving({})`", diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs index 2eaeb0df7fb..3c7f82a315a 100644 --- a/src/libsyntax/ext/deriving/cmp/eq.rs +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -27,10 +27,12 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, // any fields are not equal or if the enum variants are different fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> Gc<Expr> { 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> { cs_or(|cx, span, _, _| cx.expr_bool(span, true), + |cx, span, _, _| cx.expr_bool(span, true), cx, span, substr) } diff --git a/src/libsyntax/ext/deriving/cmp/ord.rs b/src/libsyntax/ext/deriving/cmp/ord.rs index c8edf5c4157..52a180deffb 100644 --- a/src/libsyntax/ext/deriving/cmp/ord.rs +++ b/src/libsyntax/ext/deriving/cmp/ord.rs @@ -35,7 +35,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt, args: vec!(borrowed_self()), ret_ty: Literal(Path::new(vec!("bool"))), attributes: attrs, - on_nonmatching: NonMatchesExplode, + on_nonmatching: NonMatchesCollapseWithTags, combine_substructure: combine_substructure(|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()], ret_ty: ret_ty, attributes: attrs, - on_nonmatching: NonMatchesExplode, + on_nonmatching: NonMatchesCollapseWithTags, combine_substructure: combine_substructure(|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) } +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, substr: &Substructure) -> Gc<Expr> { let test_id = cx.ident_of("__test"); @@ -147,7 +165,14 @@ pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, // later one. [(self_var, _, _), (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) @@ -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, (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) } diff --git a/src/libsyntax/ext/deriving/cmp/totaleq.rs b/src/libsyntax/ext/deriving/cmp/totaleq.rs index 09aa24f9bfb..3a6b1fa218a 100644 --- a/src/libsyntax/ext/deriving/cmp/totaleq.rs +++ b/src/libsyntax/ext/deriving/cmp/totaleq.rs @@ -33,6 +33,7 @@ pub fn expand_deriving_totaleq(cx: &mut ExtCtxt, 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, span, substr) diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs index 24785a026d1..0e4b786857b 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -41,7 +41,7 @@ pub fn expand_deriving_totalord(cx: &mut ExtCtxt, args: vec!(borrowed_self()), ret_ty: Literal(Path::new(vec!("std", "cmp", "Ordering"))), attributes: attrs, - on_nonmatching: NonMatchesExplode, + on_nonmatching: NonMatchesCollapseWithTags, combine_substructure: combine_substructure(|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))) } +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, substr: &Substructure) -> Gc<Expr> { 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, (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) } diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index eebf55033b3..039e4e2fb57 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -135,7 +135,7 @@ //! }]) //! ~~~ //! -//! For `C1 {x}` and `C1 {x}` , +//! For `C1 {x}` and `C1 {x}`, //! //! ~~~text //! EnumMatching(1, <ast::Variant for C1>, @@ -150,14 +150,20 @@ //! For `C0(a)` and `C1 {x}` , //! //! ~~~text -//! EnumNonMatching(~[(0, <ast::Variant for B0>, -//! ~[(<span of int>, None, <expr for &a>)]), -//! (1, <ast::Variant for B1>, -//! ~[(<span of x>, Some(<ident of x>), -//! <expr for &other.x>)])]) +//! EnumNonMatchingCollapsed( +//! ~[<ident of self>, <ident of __arg_1>], +//! &[<ast::Variant for C0>, <ast::Variant for C1>], +//! &[<ident for self index value>, <ident of __arg_1 index value>]) //! ~~~ //! -//! (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 //! @@ -172,7 +178,6 @@ //! (<ident of C1>, <span of C1>, //! Named(~[(<ident of x>, <span of x>)]))]) //! ~~~ -//! use std::cell::RefCell; use std::gc::{Gc, GC}; @@ -215,9 +220,18 @@ pub struct TraitDef<'a> { #[deriving(PartialEq, Eq)] pub enum HandleNonMatchingEnums { - NonMatchesCollapse, // handle all non-matches via one `_ => ..` clause - NonMatchesExplode, // handle via n^k cases for n variants and k self-args - NonMatchHandlingIrrelevant, // cannot encounter two enums of Self type + /// handle all non-matches via one `_ => ..` clause + NonMatchesCollapse, + + /// 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> { @@ -308,6 +322,16 @@ pub enum SubstructureFields<'a> { EnumNonMatching(&'a [(uint, P<ast::Variant>, 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. StaticStruct(&'a ast::StructDef, StaticFields), /// A static method where Self is an enum. @@ -324,14 +348,16 @@ pub type CombineSubstructureFunc<'a> = |&mut ExtCtxt, Span, &Substructure|: 'a -> Gc<Expr>; /** -Deal with non-matching enum variants, the arguments are a list -representing each variant: (variant index, ast::Variant instance, -[variant fields]), and a list of the nonself args of the type +Deal with non-matching enum variants. The tuple is a list of +identifiers (one for each Self argument, which could be any of the +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, Span, - &[(uint, P<ast::Variant>, Vec<(Span, Option<Ident>, Gc<Expr>)>)], + (&[Ident], &[Ident]), &[Gc<Expr>]|: 'a -> 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> { fn call_substructure_method(&self, cx: &mut ExtCtxt, @@ -769,27 +804,32 @@ impl<'a> MethodDef<'a> { ~~~ #[deriving(PartialEq)] enum A { - A1 + A1, A2(int) } - // is equivalent to (with on_nonmatching == NonMatchesExplode) + // is equivalent to impl PartialEq for A { - fn eq(&self, __arg_1: &A) { - match *self { - A1 => match *__arg_1 { - A1 => true - A2(ref __arg_1_1) => false - }, - A2(self_1) => match *__arg_1 { - A1 => false, - A2(ref __arg_1_1) => self_1.eq(__arg_1_1) + fn eq(&self, __arg_1: &A) -> ::bool { + match (&*self, &*__arg_1) { + (&A1, &A1) => true, + (&A2(ref __self_0), + &A2(ref __arg_1_0)) => (*__self_0).eq(&(*__arg_1_0)), + _ => { + let __self_vi = match *self { A1(..) => 0u, A2(..) => 1u }; + let __arg_1_vi = match *__arg_1 { A1(..) => 0u, A2(..) => 1u }; + 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, cx: &mut ExtCtxt, @@ -800,9 +840,15 @@ impl<'a> MethodDef<'a> { nonself_args: &[Gc<Expr>]) -> Gc<Expr> { let mut matches = Vec::new(); - self.build_enum_match(cx, trait_, enum_def, type_ident, - self_args, nonself_args, - None, &mut matches, 0) + match self.on_nonmatching { + NonMatchesCollapseWithTags => + 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(); + assert!(self.on_nonmatching == NonMatchesCollapse || + self.on_nonmatching == NonMatchesExplode || + self.on_nonmatching == NonMatchHandlingIrrelevant); + // the code for nonmatching variants only matters when // we've seen at least one other variant already 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, cx: &mut ExtCtxt, 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>, base: Gc<Expr>, enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, trait_span: Span, substructure: &Substructure) @@ -1210,9 +1548,12 @@ pub fn cs_fold(use_foldl: bool, }) } }, - EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span, - *all_enums, - substructure.nonself_args), + EnumNonMatching(ref all_enums) => + enum_nonmatch_f(cx, trait_span, *all_enums, + substructure.nonself_args), + EnumNonMatchingCollapsed(ref all_args, _, tuple) => + enum_nonmatch_g(cx, trait_span, (all_args.as_slice(), tuple), + substructure.nonself_args), StaticEnum(..) | StaticStruct(..) => { 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] pub fn cs_same_method(f: |&mut ExtCtxt, Span, Vec<Gc<Expr>>| -> Gc<Expr>, enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, trait_span: Span, 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) }, - EnumNonMatching(ref all_enums) => enum_nonmatch_f(cx, trait_span, - *all_enums, - substructure.nonself_args), + EnumNonMatching(ref all_enums) => + enum_nonmatch_f(cx, trait_span, *all_enums, + 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(..) => { 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>, base: Gc<Expr>, enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, trait_span: Span, substructure: &Substructure) @@ -1286,6 +1632,7 @@ pub fn cs_same_method_fold(use_foldl: bool, } }, enum_nonmatch_f, + enum_nonmatch_g, cx, trait_span, substructure) } @@ -1296,6 +1643,7 @@ on all the fields. #[inline] pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>, enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, trait_span: Span, substructure: &Substructure) -> Gc<Expr> { cs_same_method_fold( @@ -1308,25 +1656,30 @@ pub fn cs_binop(binop: ast::BinOp, base: Gc<Expr>, }, base, enum_nonmatch_f, + enum_nonmatch_g, cx, trait_span, substructure) } /// cs_binop with binop == or #[inline] pub fn cs_or(enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, span: Span, substructure: &Substructure) -> Gc<Expr> { cs_binop(ast::BiOr, cx.expr_bool(span, false), enum_nonmatch_f, + enum_nonmatch_g, cx, span, substructure) } /// cs_binop with binop == and #[inline] pub fn cs_and(enum_nonmatch_f: EnumNonMatchFunc, + enum_nonmatch_g: EnumNonMatchCollapsedFunc, cx: &mut ExtCtxt, span: Span, substructure: &Substructure) -> Gc<Expr> { cs_binop(ast::BiAnd, cx.expr_bool(span, true), enum_nonmatch_f, + enum_nonmatch_g, cx, span, substructure) } diff --git a/src/libsyntax/ext/deriving/show.rs b/src/libsyntax/ext/deriving/show.rs index 722541d7dae..f01beeb7959 100644 --- a/src/libsyntax/ext/deriving/show.rs +++ b/src/libsyntax/ext/deriving/show.rs @@ -67,7 +67,8 @@ fn show_substructure(cx: &mut ExtCtxt, span: Span, Struct(_) => substr.type_ident, EnumMatching(_, v, _) => v.node.name, - EnumNonMatching(..) | StaticStruct(..) | StaticEnum(..) => { + EnumNonMatching(..) | EnumNonMatchingCollapsed(..) | + StaticStruct(..) | StaticEnum(..) => { cx.span_bug(span, "nonsensical .fields in `#[deriving(Show)]`") } }; diff --git a/src/libsyntax/ext/deriving/zero.rs b/src/libsyntax/ext/deriving/zero.rs index 1d4f4c78fc0..59a0d1ed5ce 100644 --- a/src/libsyntax/ext/deriving/zero.rs +++ b/src/libsyntax/ext/deriving/zero.rs @@ -57,6 +57,10 @@ pub fn expand_deriving_zero(cx: &mut ExtCtxt, "Non-matching enum \ variant in \ deriving(Zero)"), + |cx, span, _, _| cx.span_bug(span, + "Non-matching enum \ + variant in \ + deriving(Zero)"), cx, span, substr) }) } |
