From 44acdad5f8974206d871bb1134f67368bdb236ee Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 21 Jul 2013 00:37:47 +1000 Subject: [PATCH 1/7] libsyntax/ext/deriving/cmp/* was ignored by the build system. --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index d78176e569999..a9a41a073d033 100644 --- a/Makefile.in +++ b/Makefile.in @@ -297,7 +297,7 @@ COMPILER_INPUTS := $(wildcard $(addprefix $(S)src/librustc/, \ LIBSYNTAX_CRATE := $(S)src/libsyntax/syntax.rs LIBSYNTAX_INPUTS := $(wildcard $(addprefix $(S)src/libsyntax/, \ - *.rs */*.rs */*/*.rs)) + *.rs */*.rs */*/*.rs */*/*/*.rs)) DRIVER_CRATE := $(S)src/driver/driver.rs From 8407ec9fedccdd410dee2bca67651ce245ed5cd3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 28 Jul 2013 01:16:30 +1000 Subject: [PATCH 2/7] syntax: make #[deriving(TotalOrd)] lazy. Previously it would call: f(sf1.cmp(&of1), f(sf2.cmp(&of2), ...)) (where s/of1 = 'self/other field 1', and f was std::cmp::lexical_ordering) This meant that every .cmp subcall got evaluated when calling a derived TotalOrd.cmp. This corrects this to use let test = sf1.cmp(&of1); if test == Equal { let test = sf2.cmp(&of2); if test == Equal { // ... } else { test } } else { test } This gives a lexical ordering by short-circuiting on the first comparison that is not Equal. --- src/libstd/cmp.rs | 1 - src/libsyntax/ext/deriving/cmp/totalord.rs | 63 ++++++++++++++++------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/libstd/cmp.rs b/src/libstd/cmp.rs index 43a632562b2b7..b66f89e83415e 100644 --- a/src/libstd/cmp.rs +++ b/src/libstd/cmp.rs @@ -153,7 +153,6 @@ pub fn cmp2( Return `o1` if it is not `Equal`, otherwise `o2`. Simulates the lexical ordering on a type `(int, int)`. */ -// used in deriving code in libsyntax #[inline] pub fn lexical_ordering(o1: Ordering, o2: Ordering) -> Ordering { match o1 { diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs index 01dacdfe453ee..001e92355282f 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use ast; use ast::{MetaItem, item, expr}; use codemap::span; use ext::base::ExtCtxt; @@ -40,40 +41,70 @@ pub fn expand_deriving_totalord(cx: @ExtCtxt, } -pub fn ordering_const(cx: @ExtCtxt, span: span, cnst: Ordering) -> @expr { +pub fn ordering_const(cx: @ExtCtxt, span: span, cnst: Ordering) -> ast::Path { let cnst = match cnst { Less => "Less", Equal => "Equal", Greater => "Greater" }; - cx.expr_path( - cx.path_global(span, - ~[cx.ident_of("std"), - cx.ident_of("cmp"), - cx.ident_of(cnst)])) + cx.path_global(span, + ~[cx.ident_of("std"), + cx.ident_of("cmp"), + cx.ident_of(cnst)]) } pub fn cs_cmp(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr { + let test_id = cx.ident_of("__test"); + let equals_path = ordering_const(cx, span, Equal); + /* + Builds: + + let __test = self_field1.cmp(&other_field2); + if other == ::std::cmp::Equal { + let __test = self_field2.cmp(&other_field2); + if __test == ::std::cmp::Equal { + ... + } else { + __test + } + } else { + __test + } + + FIXME #6449: These `if`s could/should be `match`es. + */ cs_same_method_fold( - // foldr (possibly) nests the matches in lexical_ordering better + // foldr nests the if-elses correctly, leaving the first field + // as the outermost one, and the last as the innermost. false, |cx, span, old, new| { - cx.expr_call_global(span, - ~[cx.ident_of("std"), - cx.ident_of("cmp"), - cx.ident_of("lexical_ordering")], - ~[old, new]) + // let __test = new; + // if __test == ::std::cmp::Equal { + // old + // } else { + // __test + // } + + let assign = cx.stmt_let(span, false, test_id, new); + + let cond = cx.expr_binary(span, ast::eq, + cx.expr_ident(span, test_id), + cx.expr_path(equals_path.clone())); + let if_ = cx.expr_if(span, + cond, + old, Some(cx.expr_ident(span, test_id))); + cx.expr_block(cx.block(span, ~[assign], Some(if_))) }, - ordering_const(cx, span, Equal), + cx.expr_path(equals_path.clone()), |cx, span, list, _| { match list { // an earlier nonmatching variant is Less than a - // later one + // later one. [(self_var, _, _), - (other_var, _, _)] => ordering_const(cx, span, - self_var.cmp(&other_var)), + (other_var, _, _)] => cx.expr_path(ordering_const(cx, span, + self_var.cmp(&other_var))), _ => cx.span_bug(span, "Not exactly 2 arguments in `deriving(TotalOrd)`") } }, From 88620c25f5a2b5e17f3bff1e70ed2fbee97c2217 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 28 Jul 2013 01:25:30 +1000 Subject: [PATCH 3/7] std: implement Total{Ord,Eq} for pointers. --- src/libstd/borrow.rs | 12 ++++++++++++ src/libstd/managed.rs | 25 ++++++++++++++++++++++++- src/libstd/owned.rs | 14 +++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/libstd/borrow.rs b/src/libstd/borrow.rs index 9e3a3a28fe8ce..6c3d4c5f1fbea 100644 --- a/src/libstd/borrow.rs +++ b/src/libstd/borrow.rs @@ -58,3 +58,15 @@ impl<'self, T: Ord> Ord for &'self T { *(*self) > *(*other) } } + +#[cfg(not(test))] +impl<'self, T: TotalOrd> TotalOrd for &'self T { + #[inline] + fn cmp(&self, other: & &'self T) -> Ordering { (**self).cmp(*other) } +} + +#[cfg(not(test))] +impl<'self, T: TotalEq> TotalEq for &'self T { + #[inline] + fn equals(&self, other: & &'self T) -> bool { (**self).equals(*other) } +} diff --git a/src/libstd/managed.rs b/src/libstd/managed.rs index bd4dc69537cb1..57230b2fd247d 100644 --- a/src/libstd/managed.rs +++ b/src/libstd/managed.rs @@ -12,7 +12,7 @@ use ptr::to_unsafe_ptr; -#[cfg(not(test))] use cmp::{Eq, Ord}; +#[cfg(not(test))] use cmp::*; pub static RC_MANAGED_UNIQUE : uint = (-2) as uint; pub static RC_IMMORTAL : uint = 0x77777777; @@ -71,6 +71,29 @@ impl Ord for @mut T { fn gt(&self, other: &@mut T) -> bool { *(*self) > *(*other) } } +#[cfg(not(test))] +impl TotalOrd for @T { + #[inline] + fn cmp(&self, other: &@T) -> Ordering { (**self).cmp(*other) } +} + +#[cfg(not(test))] +impl TotalOrd for @mut T { + #[inline] + fn cmp(&self, other: &@mut T) -> Ordering { (**self).cmp(*other) } +} + +#[cfg(not(test))] +impl TotalEq for @T { + #[inline] + fn equals(&self, other: &@T) -> bool { (**self).equals(*other) } +} + +#[cfg(not(test))] +impl TotalEq for @mut T { + #[inline] + fn equals(&self, other: &@mut T) -> bool { (**self).equals(*other) } +} #[test] fn test() { let x = @3; diff --git a/src/libstd/owned.rs b/src/libstd/owned.rs index e7a6e38fdb088..424c4fd6b2f44 100644 --- a/src/libstd/owned.rs +++ b/src/libstd/owned.rs @@ -10,7 +10,7 @@ //! Operations on unique pointer types -#[cfg(not(test))] use cmp::{Eq, Ord}; +#[cfg(not(test))] use cmp::*; #[cfg(not(test))] impl Eq for ~T { @@ -31,3 +31,15 @@ impl Ord for ~T { #[inline] fn gt(&self, other: &~T) -> bool { *(*self) > *(*other) } } + +#[cfg(not(test))] +impl TotalOrd for ~T { + #[inline] + fn cmp(&self, other: &~T) -> Ordering { (**self).cmp(*other) } +} + +#[cfg(not(test))] +impl TotalEq for ~T { + #[inline] + fn equals(&self, other: &~T) -> bool { (**self).equals(*other) } +} From 5afbee00db8983c52c2511ff312d42d4dfe918b3 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 28 Jul 2013 01:45:30 +1000 Subject: [PATCH 4/7] syntax: pass options to #[deriving] implementations. For example, in #[deriving(SomeTrait(foo, bar="baz", qux))], the bracketed data will get passed to the SomeTrait implementation. It supports both the list notation and the #[deriving(SomeTrait="a literal")] notation. --- src/libsyntax/ext/deriving/clone.rs | 7 ++ src/libsyntax/ext/deriving/cmp/eq.rs | 2 + src/libsyntax/ext/deriving/cmp/ord.rs | 2 + src/libsyntax/ext/deriving/cmp/totaleq.rs | 2 + src/libsyntax/ext/deriving/cmp/totalord.rs | 2 + src/libsyntax/ext/deriving/decodable.rs | 4 +- src/libsyntax/ext/deriving/encodable.rs | 2 + src/libsyntax/ext/deriving/iter_bytes.rs | 5 +- src/libsyntax/ext/deriving/mod.rs | 100 ++++++++++-------- src/libsyntax/ext/deriving/rand.rs | 5 +- src/libsyntax/ext/deriving/to_str.rs | 4 + src/libsyntax/ext/deriving/zero.rs | 5 +- .../compile-fail/deriving-takes-no-options.rs | 21 ++++ 13 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 src/test/compile-fail/deriving-takes-no-options.rs diff --git a/src/libsyntax/ext/deriving/clone.rs b/src/libsyntax/ext/deriving/clone.rs index 02dcb2cdbc9eb..51aee6ea45eb4 100644 --- a/src/libsyntax/ext/deriving/clone.rs +++ b/src/libsyntax/ext/deriving/clone.rs @@ -13,12 +13,16 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_clone(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "Clone"); + let trait_def = TraitDef { path: Path::new(~["std", "clone", "Clone"]), additional_bounds: ~[], @@ -41,9 +45,12 @@ pub fn expand_deriving_clone(cx: @ExtCtxt, pub fn expand_deriving_deep_clone(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "DeepClone"); + let trait_def = TraitDef { path: Path::new(~["std", "clone", "DeepClone"]), additional_bounds: ~[], diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs index a7d9db59130f3..403c7178c496f 100644 --- a/src/libsyntax/ext/deriving/cmp/eq.rs +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -13,9 +13,11 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_eq(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { // structures are equal if all fields are equal, and non equal, if diff --git a/src/libsyntax/ext/deriving/cmp/ord.rs b/src/libsyntax/ext/deriving/cmp/ord.rs index d532eedd2918e..5593db0e42475 100644 --- a/src/libsyntax/ext/deriving/cmp/ord.rs +++ b/src/libsyntax/ext/deriving/cmp/ord.rs @@ -14,9 +14,11 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_ord(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { macro_rules! md ( diff --git a/src/libsyntax/ext/deriving/cmp/totaleq.rs b/src/libsyntax/ext/deriving/cmp/totaleq.rs index 8285de1d56198..eae1e73e4ab00 100644 --- a/src/libsyntax/ext/deriving/cmp/totaleq.rs +++ b/src/libsyntax/ext/deriving/cmp/totaleq.rs @@ -13,9 +13,11 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_totaleq(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { fn cs_equals(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr { diff --git a/src/libsyntax/ext/deriving/cmp/totalord.rs b/src/libsyntax/ext/deriving/cmp/totalord.rs index 001e92355282f..543a914a2c18f 100644 --- a/src/libsyntax/ext/deriving/cmp/totalord.rs +++ b/src/libsyntax/ext/deriving/cmp/totalord.rs @@ -14,10 +14,12 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; use std::cmp::{Ordering, Equal, Less, Greater}; pub fn expand_deriving_totalord(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { let trait_def = TraitDef { diff --git a/src/libsyntax/ext/deriving/decodable.rs b/src/libsyntax/ext/deriving/decodable.rs index 59b7da16c2bcb..e8a8bdbbaec70 100644 --- a/src/libsyntax/ext/deriving/decodable.rs +++ b/src/libsyntax/ext/deriving/decodable.rs @@ -10,7 +10,7 @@ /*! The compiler code necessary for #[deriving(Decodable)]. See -encodable.rs for more. +encodable.rs for more details. */ use std::vec; @@ -20,9 +20,11 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_decodable(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { let trait_def = TraitDef { diff --git a/src/libsyntax/ext/deriving/encodable.rs b/src/libsyntax/ext/deriving/encodable.rs index f16fea7bb0614..1d650130bbfb3 100644 --- a/src/libsyntax/ext/deriving/encodable.rs +++ b/src/libsyntax/ext/deriving/encodable.rs @@ -80,9 +80,11 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_encodable(cx: @ExtCtxt, span: span, + _options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { let trait_def = TraitDef { diff --git a/src/libsyntax/ext/deriving/iter_bytes.rs b/src/libsyntax/ext/deriving/iter_bytes.rs index b0f442ee63805..65c68d3579501 100644 --- a/src/libsyntax/ext/deriving/iter_bytes.rs +++ b/src/libsyntax/ext/deriving/iter_bytes.rs @@ -13,12 +13,15 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; - +use ext::deriving::DerivingOptions; pub fn expand_deriving_iter_bytes(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "IterBytes"); + let trait_def = TraitDef { path: Path::new(~["std", "to_bytes", "IterBytes"]), additional_bounds: ~[], diff --git a/src/libsyntax/ext/deriving/mod.rs b/src/libsyntax/ext/deriving/mod.rs index cde7dcc5dbe2a..a08577cfb3b9e 100644 --- a/src/libsyntax/ext/deriving/mod.rs +++ b/src/libsyntax/ext/deriving/mod.rs @@ -18,8 +18,7 @@ library. */ -use ast::{enum_def, ident, item, Generics, struct_def}; -use ast::{MetaItem, MetaList, MetaNameValue, MetaWord}; +use ast::{item, lit, MetaItem, MetaList, MetaNameValue, MetaWord}; use ext::base::ExtCtxt; use ext::build::AstBuilder; use codemap::span; @@ -44,19 +43,6 @@ pub mod totalord; pub mod generic; -pub type ExpandDerivingStructDefFn<'self> = &'self fn(@ExtCtxt, - span, - x: &struct_def, - ident, - y: &Generics) - -> @item; -pub type ExpandDerivingEnumDefFn<'self> = &'self fn(@ExtCtxt, - span, - x: &enum_def, - ident, - y: &Generics) - -> @item; - pub fn expand_meta_deriving(cx: @ExtCtxt, _span: span, mitem: @MetaItem, @@ -73,40 +59,62 @@ pub fn expand_meta_deriving(cx: @ExtCtxt, } MetaList(_, ref titems) => { do titems.rev_iter().fold(in_items) |in_items, &titem| { - match titem.node { - MetaNameValue(tname, _) | - MetaList(tname, _) | - MetaWord(tname) => { - macro_rules! expand(($func:path) => ($func(cx, titem.span, - titem, in_items))); - match tname.as_slice() { - "Clone" => expand!(clone::expand_deriving_clone), - "DeepClone" => expand!(clone::expand_deriving_deep_clone), - - "IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes), - - "Encodable" => expand!(encodable::expand_deriving_encodable), - "Decodable" => expand!(decodable::expand_deriving_decodable), - - "Eq" => expand!(eq::expand_deriving_eq), - "TotalEq" => expand!(totaleq::expand_deriving_totaleq), - "Ord" => expand!(ord::expand_deriving_ord), - "TotalOrd" => expand!(totalord::expand_deriving_totalord), - - "Rand" => expand!(rand::expand_deriving_rand), - - "ToStr" => expand!(to_str::expand_deriving_to_str), - "Zero" => expand!(zero::expand_deriving_zero), - - ref tname => { - cx.span_err(titem.span, fmt!("unknown \ - `deriving` trait: `%s`", *tname)); - in_items - } - } + let (name, options) = match titem.node { + MetaNameValue(name, ref lit) => (name, Lit(lit)), + MetaList(name, ref list) => (name, List(*list)), + MetaWord(name) => (name, NoOptions), + }; + + macro_rules! expand(($func:path) => ($func(cx, titem.span, options, + titem, in_items))); + match name.as_slice() { + "Clone" => expand!(clone::expand_deriving_clone), + "DeepClone" => expand!(clone::expand_deriving_deep_clone), + + "IterBytes" => expand!(iter_bytes::expand_deriving_iter_bytes), + + "Encodable" => expand!(encodable::expand_deriving_encodable), + "Decodable" => expand!(decodable::expand_deriving_decodable), + + "Eq" => expand!(eq::expand_deriving_eq), + "TotalEq" => expand!(totaleq::expand_deriving_totaleq), + "Ord" => expand!(ord::expand_deriving_ord), + "TotalOrd" => expand!(totalord::expand_deriving_totalord), + + "Rand" => expand!(rand::expand_deriving_rand), + + "ToStr" => expand!(to_str::expand_deriving_to_str), + "Zero" => expand!(zero::expand_deriving_zero), + + _ => { + cx.span_err(titem.span, fmt!("unknown `deriving` trait: `%s`", name)); + in_items } } } } } } + +/// Summary of `#[deriving(SomeTrait(foo, option="bar"))]` and +/// `#[deriving(SomeTrait="foo")]`. +pub enum DerivingOptions<'self> { + NoOptions, + Lit(&'self lit), + List(&'self [@MetaItem]) +} + +impl<'self> DerivingOptions<'self> { + /// Emits a warning when there are options and the deriving + /// implementation doesn't use them (i.e. it warns if `self` is + /// not `NoOptions`.) + pub fn unused_options_maybe_warn(&self, cx: @ExtCtxt, span: span, deriving_name: &str) { + match *self { + NoOptions => {}, + _ => { + cx.span_warn(span, + fmt!("`#[deriving(%s)]` does not use any options.", deriving_name)); + } + } + } +} diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index 2966a8c114daf..771fe1c320b1b 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -14,14 +14,17 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::{AstBuilder, Duplicate}; use ext::deriving::generic::*; - +use ext::deriving::DerivingOptions; use std::vec; pub fn expand_deriving_rand(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "Rand"); + let trait_def = TraitDef { path: Path::new(~["std", "rand", "Rand"]), additional_bounds: ~[], diff --git a/src/libsyntax/ext/deriving/to_str.rs b/src/libsyntax/ext/deriving/to_str.rs index 3425352ff0c5a..dc3363143ca97 100644 --- a/src/libsyntax/ext/deriving/to_str.rs +++ b/src/libsyntax/ext/deriving/to_str.rs @@ -14,12 +14,16 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; +use ext::deriving::DerivingOptions; pub fn expand_deriving_to_str(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "ToStr"); + let trait_def = TraitDef { path: Path::new(~["std", "to_str", "ToStr"]), additional_bounds: ~[], diff --git a/src/libsyntax/ext/deriving/zero.rs b/src/libsyntax/ext/deriving/zero.rs index 3c9e842473c1c..0a0f57a9d5d10 100644 --- a/src/libsyntax/ext/deriving/zero.rs +++ b/src/libsyntax/ext/deriving/zero.rs @@ -13,14 +13,17 @@ use codemap::span; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; - +use ext::deriving::DerivingOptions; use std::vec; pub fn expand_deriving_zero(cx: @ExtCtxt, span: span, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + options.unused_options_maybe_warn(cx, span, "Zero"); + let trait_def = TraitDef { path: Path::new(~["std", "num", "Zero"]), additional_bounds: ~[], diff --git a/src/test/compile-fail/deriving-takes-no-options.rs b/src/test/compile-fail/deriving-takes-no-options.rs new file mode 100644 index 0000000000000..62ab12eb87da1 --- /dev/null +++ b/src/test/compile-fail/deriving-takes-no-options.rs @@ -0,0 +1,21 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[deriving(Zero="no options")] //~ WARNING does not use any options +struct A { a: int } + +#[deriving(Rand(foo))] //~ WARNING does not use any options +struct B { b: int } + +// At least one error is needed so that compilation fails +#[static_assert] +static b: bool = false; //~ ERROR static assertion failed + +fn main() {} From 6b098f0640df3befc9a59233feef4690600fae38 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 28 Jul 2013 02:45:30 +1000 Subject: [PATCH 5/7] syntax: allow cmp derived traits to adjust the implementation on normal structs. Example: #[deriving(Eq(test_order(y), ignore(z, w)), TotalOrd(reverse(x)))] struct Foo { x: uint, y: uint, z: uint, w: uint } This allows several things to be controlled: - `test_order` prioritises certain fields (e.g. the cheapest tests be placed first), i.e. y is tested first, and then x. This will not change the result of `Eq` & `TotalEq` impls, but can change `Ord` & `TotalOrd` - `reverse` reverses the result of Ord & TotalOrd comparisons for the given fields (so it is as if x = 1 > x = 2 in the example above) - `ignore` completely removes the fields from the comparison, e.g. `z` and `w` are not considered at all for the `Eq` impl of `Foo` above. Only `Eq` and `Ord` accept `ignore` (since a `Total*` impl ignoring some fields would not be total), and only `Ord` and `TotalOrd` accept `reverse`. All 4 traits accept `test_order`. Any other option, or using a syntax different to `#[deriving(Trait(opt(field1, field2, ..)))]`, is an error. Using any of these options on an enum, or a tuple or unit struct is also an error. --- src/libsyntax/ext/base.rs | 4 + src/libsyntax/ext/deriving/cmp/eq.rs | 20 +- src/libsyntax/ext/deriving/cmp/mod.rs | 381 ++++++++++++++++++ src/libsyntax/ext/deriving/cmp/ord.rs | 18 +- src/libsyntax/ext/deriving/cmp/totaleq.rs | 12 +- src/libsyntax/ext/deriving/cmp/totalord.rs | 12 +- src/libsyntax/ext/deriving/generic.rs | 15 +- src/libsyntax/ext/deriving/mod.rs | 19 +- src/libsyntax/ext/deriving/ty.rs | 2 +- src/test/compile-fail/deriving-cmp-options.rs | 47 +++ src/test/run-pass/deriving-cmp-options.rs | 153 +++++++ 11 files changed, 647 insertions(+), 36 deletions(-) create mode 100644 src/libsyntax/ext/deriving/cmp/mod.rs create mode 100644 src/test/compile-fail/deriving-cmp-options.rs create mode 100644 src/test/run-pass/deriving-cmp-options.rs diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1dda2493da20a..a0a5cf4fe8e51 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -282,6 +282,10 @@ impl ExtCtxt { self.print_backtrace(); self.parse_sess.span_diagnostic.span_warn(sp, msg); } + pub fn span_note(&self, sp: span, msg: &str) { + self.print_backtrace(); + self.parse_sess.span_diagnostic.span_note(sp, msg); + } pub fn span_unimpl(&self, sp: span, msg: &str) -> ! { self.print_backtrace(); self.parse_sess.span_diagnostic.span_unimpl(sp, msg); diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs index 403c7178c496f..621ee1ce4fc6d 100644 --- a/src/libsyntax/ext/deriving/cmp/eq.rs +++ b/src/libsyntax/ext/deriving/cmp/eq.rs @@ -14,17 +14,23 @@ use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::deriving::generic::*; use ext::deriving::DerivingOptions; +use ext::deriving::cmp::CmpOptions; pub fn expand_deriving_eq(cx: @ExtCtxt, span: span, - _options: DerivingOptions, + options: DerivingOptions, mitem: @MetaItem, in_items: ~[@item]) -> ~[@item] { + let mut options = match CmpOptions::parse(cx, span, "Eq", options, true, false) { + Some(o) => o, + None => return in_items + }; + // structures are equal if all fields are equal, and non equal, if // any fields are not equal or if the enum variants are different fn cs_eq(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr { cs_and(|cx, span, _, _| cx.expr_bool(span, false), - cx, span, substr) + cx, span, substr) } fn cs_ne(cx: @ExtCtxt, span: span, substr: &Substructure) -> @expr { cs_or(|cx, span, _, _| cx.expr_bool(span, true), @@ -32,7 +38,7 @@ pub fn expand_deriving_eq(cx: @ExtCtxt, } macro_rules! md ( - ($name:expr, $f:ident) => { + ($name:expr, $f:ident) => {{ MethodDef { name: $name, generics: LifetimeBounds::empty(), @@ -40,9 +46,11 @@ pub fn expand_deriving_eq(cx: @ExtCtxt, args: ~[borrowed_self()], ret_ty: Literal(Path::new(~["bool"])), const_nonmatching: true, - combine_substructure: $f - }, - } + combine_substructure: |cx, span, substr| { + options.call_substructure(cx, span, substr, $f) + } + } + }} ); let trait_def = TraitDef { diff --git a/src/libsyntax/ext/deriving/cmp/mod.rs b/src/libsyntax/ext/deriving/cmp/mod.rs new file mode 100644 index 0000000000000..3b905ef649964 --- /dev/null +++ b/src/libsyntax/ext/deriving/cmp/mod.rs @@ -0,0 +1,381 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ast::{ident, expr}; +use codemap::span; +use ast; +use ext::base::ExtCtxt; +use ext::deriving::generic::*; +use ext::deriving::{DerivingOptions, NoOptions, Lit, List}; + +use std::vec; + +pub mod eq; +pub mod totaleq; +pub mod ord; +pub mod totalord; + +pub struct CmpOptions_ { + /// Ensure that we don't show errors several times. + shown_errors: bool, + test_order: ~[(ident, span)], + ignore: ~[(ident, span)], + reverse: ~[(ident, span)] +} +pub struct CmpOptions(Option); + +impl CmpOptions { + pub fn parse(cx: @ExtCtxt, _span: span, trait_name: &str, + options: DerivingOptions, + allow_ignore: bool, allow_reverse: bool) -> Option { + // Trait(