Rollup merge of #32199 - nikomatsakis:limiting-constants-in-patterns-2, r=pnkfelix
Restrict constants in patterns This implements [RFC 1445](https://github.com/rust-lang/rfcs/blob/master/text/1445-restrict-constants-in-patterns.md). The primary change is to limit the types of constants used in patterns to those that *derive* `Eq` (note that implementing `Eq` is not sufficient). This has two main effects: 1. Floating point constants are linted, and will eventually be disallowed. This is because floating point constants do not implement `Eq` but only `PartialEq`. This check replaces the existing special case code that aimed to detect the use of `NaN`. 2. Structs and enums must derive `Eq` to be usable within a match. This is a [breaking-change]: if you encounter a problem, you are most likely using a constant in an expression where the type of the constant is some struct that does not currently implement `Eq`. Something like the following: ```rust struct SomeType { ... } const SOME_CONST: SomeType = ...; match foo { SOME_CONST => ... } ``` The easiest and most future compatible fix is to annotate the type in question with `#[derive(Eq)]` (note that merely *implementing* `Eq` is not enough, it must be *derived*): ```rust struct SomeType { ... } const SOME_CONST: SomeType = ...; match foo { SOME_CONST => ... } ``` Another good option is to rewrite the match arm to use an `if` condition (this is also particularly good for floating point types, which implement `PartialEq` but not `Eq`): ```rust match foo { c if c == SOME_CONST => ... } ``` Finally, a third alternative is to tag the type with `#[structural_match]`; but this is not recommended, as the attribute is never expected to be stabilized. Please see RFC #1445 for more details. cc https://github.com/rust-lang/rust/issues/31434 r? @pnkfelix
This commit is contained in:
commit
128b2ad829
|
@ -136,6 +136,19 @@ declare_lint! {
|
||||||
"type parameter default erroneously allowed in invalid location"
|
"type parameter default erroneously allowed in invalid location"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
pub ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
|
||||||
|
Warn,
|
||||||
|
"floating-point constants cannot be used in patterns"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
pub ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
|
||||||
|
Deny,
|
||||||
|
"constants of struct or enum type can only be used in a pattern if \
|
||||||
|
the struct or enum has `#[derive(PartialEq, Eq)]`"
|
||||||
|
}
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
|
pub MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
|
||||||
Deny,
|
Deny,
|
||||||
|
@ -193,6 +206,8 @@ impl LintPass for HardwiredLints {
|
||||||
PRIVATE_IN_PUBLIC,
|
PRIVATE_IN_PUBLIC,
|
||||||
INACCESSIBLE_EXTERN_CRATE,
|
INACCESSIBLE_EXTERN_CRATE,
|
||||||
INVALID_TYPE_PARAM_DEFAULT,
|
INVALID_TYPE_PARAM_DEFAULT,
|
||||||
|
ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
|
||||||
|
ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
|
||||||
MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
|
MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT,
|
||||||
CONST_ERR,
|
CONST_ERR,
|
||||||
RAW_POINTER_DERIVE,
|
RAW_POINTER_DERIVE,
|
||||||
|
|
|
@ -478,15 +478,24 @@ impl<'a, 'tcx> Folder for StaticInliner<'a, 'tcx> {
|
||||||
Some(Def::Const(did)) => {
|
Some(Def::Const(did)) => {
|
||||||
let substs = Some(self.tcx.node_id_item_substs(pat.id).substs);
|
let substs = Some(self.tcx.node_id_item_substs(pat.id).substs);
|
||||||
if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) {
|
if let Some((const_expr, _)) = lookup_const_by_id(self.tcx, did, substs) {
|
||||||
const_expr_to_pat(self.tcx, const_expr, pat.span).map(|new_pat| {
|
match const_expr_to_pat(self.tcx, const_expr, pat.id, pat.span) {
|
||||||
|
Ok(new_pat) => {
|
||||||
if let Some(ref mut renaming_map) = self.renaming_map {
|
if let Some(ref mut map) = self.renaming_map {
|
||||||
// Record any renamings we do here
|
// Record any renamings we do here
|
||||||
record_renamings(const_expr, &pat, renaming_map);
|
record_renamings(const_expr, &pat, map);
|
||||||
|
}
|
||||||
|
new_pat
|
||||||
}
|
}
|
||||||
|
Err(def_id) => {
|
||||||
new_pat
|
self.failed = true;
|
||||||
})
|
self.tcx.sess.span_err(
|
||||||
|
pat.span,
|
||||||
|
&format!("constants of the type `{}` \
|
||||||
|
cannot be used in patterns",
|
||||||
|
self.tcx.item_path_str(def_id)));
|
||||||
|
pat
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.failed = true;
|
self.failed = true;
|
||||||
span_err!(self.tcx.sess, pat.span, E0158,
|
span_err!(self.tcx.sess, pat.span, E0158,
|
||||||
|
|
|
@ -16,6 +16,7 @@ use self::EvalHint::*;
|
||||||
|
|
||||||
use front::map as ast_map;
|
use front::map as ast_map;
|
||||||
use front::map::blocks::FnLikeNode;
|
use front::map::blocks::FnLikeNode;
|
||||||
|
use lint;
|
||||||
use middle::cstore::{self, CrateStore, InlinedItem};
|
use middle::cstore::{self, CrateStore, InlinedItem};
|
||||||
use middle::{infer, subst, traits};
|
use middle::{infer, subst, traits};
|
||||||
use middle::def::Def;
|
use middle::def::Def;
|
||||||
|
@ -323,10 +324,41 @@ impl ConstVal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
|
pub fn const_expr_to_pat(tcx: &ty::TyCtxt, expr: &Expr, pat_id: ast::NodeId, span: Span)
|
||||||
|
-> Result<P<hir::Pat>, DefId> {
|
||||||
|
let pat_ty = tcx.expr_ty(expr);
|
||||||
|
debug!("expr={:?} pat_ty={:?} pat_id={}", expr, pat_ty, pat_id);
|
||||||
|
match pat_ty.sty {
|
||||||
|
ty::TyFloat(_) => {
|
||||||
|
tcx.sess.add_lint(
|
||||||
|
lint::builtin::ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN,
|
||||||
|
pat_id,
|
||||||
|
span,
|
||||||
|
format!("floating point constants cannot be used in patterns"));
|
||||||
|
}
|
||||||
|
ty::TyEnum(adt_def, _) |
|
||||||
|
ty::TyStruct(adt_def, _) => {
|
||||||
|
if !tcx.has_attr(adt_def.did, "structural_match") {
|
||||||
|
tcx.sess.add_lint(
|
||||||
|
lint::builtin::ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN,
|
||||||
|
pat_id,
|
||||||
|
span,
|
||||||
|
format!("to use a constant of type `{}` \
|
||||||
|
in a pattern, \
|
||||||
|
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
|
||||||
|
tcx.item_path_str(adt_def.did),
|
||||||
|
tcx.item_path_str(adt_def.did)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
let pat = match expr.node {
|
let pat = match expr.node {
|
||||||
hir::ExprTup(ref exprs) =>
|
hir::ExprTup(ref exprs) =>
|
||||||
PatKind::Tup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect()),
|
PatKind::Tup(try!(exprs.iter()
|
||||||
|
.map(|expr| const_expr_to_pat(tcx, &expr,
|
||||||
|
pat_id, span))
|
||||||
|
.collect())),
|
||||||
|
|
||||||
hir::ExprCall(ref callee, ref args) => {
|
hir::ExprCall(ref callee, ref args) => {
|
||||||
let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
|
let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
|
||||||
|
@ -336,31 +368,41 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
|
||||||
let path = match def.full_def() {
|
let path = match def.full_def() {
|
||||||
Def::Struct(def_id) => def_to_path(tcx, def_id),
|
Def::Struct(def_id) => def_to_path(tcx, def_id),
|
||||||
Def::Variant(_, variant_did) => def_to_path(tcx, variant_did),
|
Def::Variant(_, variant_did) => def_to_path(tcx, variant_did),
|
||||||
Def::Fn(..) => return P(hir::Pat {
|
Def::Fn(..) => return Ok(P(hir::Pat {
|
||||||
id: expr.id,
|
id: expr.id,
|
||||||
node: PatKind::Lit(P(expr.clone())),
|
node: PatKind::Lit(P(expr.clone())),
|
||||||
span: span,
|
span: span,
|
||||||
}),
|
})),
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect();
|
let pats = try!(args.iter()
|
||||||
|
.map(|expr| const_expr_to_pat(tcx, &**expr,
|
||||||
|
pat_id, span))
|
||||||
|
.collect());
|
||||||
PatKind::TupleStruct(path, Some(pats))
|
PatKind::TupleStruct(path, Some(pats))
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprStruct(ref path, ref fields, None) => {
|
hir::ExprStruct(ref path, ref fields, None) => {
|
||||||
let field_pats = fields.iter().map(|field| codemap::Spanned {
|
let field_pats =
|
||||||
span: codemap::DUMMY_SP,
|
try!(fields.iter()
|
||||||
node: hir::FieldPat {
|
.map(|field| Ok(codemap::Spanned {
|
||||||
name: field.name.node,
|
span: codemap::DUMMY_SP,
|
||||||
pat: const_expr_to_pat(tcx, &field.expr, span),
|
node: hir::FieldPat {
|
||||||
is_shorthand: false,
|
name: field.name.node,
|
||||||
},
|
pat: try!(const_expr_to_pat(tcx, &field.expr,
|
||||||
}).collect();
|
pat_id, span)),
|
||||||
|
is_shorthand: false,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.collect());
|
||||||
PatKind::Struct(path.clone(), field_pats, false)
|
PatKind::Struct(path.clone(), field_pats, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprVec(ref exprs) => {
|
hir::ExprVec(ref exprs) => {
|
||||||
let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &expr, span)).collect();
|
let pats = try!(exprs.iter()
|
||||||
|
.map(|expr| const_expr_to_pat(tcx, &expr,
|
||||||
|
pat_id, span))
|
||||||
|
.collect());
|
||||||
PatKind::Vec(pats, None, hir::HirVec::new())
|
PatKind::Vec(pats, None, hir::HirVec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +415,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
|
||||||
Some(Def::AssociatedConst(def_id)) => {
|
Some(Def::AssociatedConst(def_id)) => {
|
||||||
let substs = Some(tcx.node_id_item_substs(expr.id).substs);
|
let substs = Some(tcx.node_id_item_substs(expr.id).substs);
|
||||||
let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
|
let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
|
||||||
return const_expr_to_pat(tcx, expr, span);
|
return const_expr_to_pat(tcx, expr, pat_id, span);
|
||||||
},
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -381,7 +423,7 @@ pub fn const_expr_to_pat(tcx: &TyCtxt, expr: &Expr, span: Span) -> P<hir::Pat> {
|
||||||
|
|
||||||
_ => PatKind::Lit(P(expr.clone()))
|
_ => PatKind::Lit(P(expr.clone()))
|
||||||
};
|
};
|
||||||
P(hir::Pat { id: expr.id, node: pat, span: span })
|
Ok(P(hir::Pat { id: expr.id, node: pat, span: span }))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal {
|
pub fn eval_const_expr(tcx: &TyCtxt, e: &Expr) -> ConstVal {
|
||||||
|
|
|
@ -252,7 +252,13 @@ impl Session {
|
||||||
let lint_id = lint::LintId::of(lint);
|
let lint_id = lint::LintId::of(lint);
|
||||||
let mut lints = self.lints.borrow_mut();
|
let mut lints = self.lints.borrow_mut();
|
||||||
match lints.get_mut(&id) {
|
match lints.get_mut(&id) {
|
||||||
Some(arr) => { arr.push((lint_id, sp, msg)); return; }
|
Some(arr) => {
|
||||||
|
let tuple = (lint_id, sp, msg);
|
||||||
|
if !arr.contains(&tuple) {
|
||||||
|
arr.push(tuple);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
lints.insert(id, vec!((lint_id, sp, msg)));
|
lints.insert(id, vec!((lint_id, sp, msg)));
|
||||||
|
|
|
@ -179,6 +179,14 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
|
||||||
id: LintId::of(OVERLAPPING_INHERENT_IMPLS),
|
id: LintId::of(OVERLAPPING_INHERENT_IMPLS),
|
||||||
reference: "issue #22889 <https://github.com/rust-lang/rust/issues/22889>",
|
reference: "issue #22889 <https://github.com/rust-lang/rust/issues/22889>",
|
||||||
},
|
},
|
||||||
|
FutureIncompatibleInfo {
|
||||||
|
id: LintId::of(ILLEGAL_FLOATING_POINT_CONSTANT_PATTERN),
|
||||||
|
reference: "RFC 1445 <https://github.com/rust-lang/rfcs/pull/1445>",
|
||||||
|
},
|
||||||
|
FutureIncompatibleInfo {
|
||||||
|
id: LintId::of(ILLEGAL_STRUCT_OR_ENUM_CONSTANT_PATTERN),
|
||||||
|
reference: "RFC 1445 <https://github.com/rust-lang/rfcs/pull/1445>",
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// We have one lint pass defined specially
|
// We have one lint pass defined specially
|
||||||
|
|
|
@ -90,9 +90,16 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
|
||||||
let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs);
|
let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs);
|
||||||
match const_eval::lookup_const_by_id(self.cx.tcx, def_id, substs) {
|
match const_eval::lookup_const_by_id(self.cx.tcx, def_id, substs) {
|
||||||
Some((const_expr, _const_ty)) => {
|
Some((const_expr, _const_ty)) => {
|
||||||
let pat = const_eval::const_expr_to_pat(self.cx.tcx, const_expr,
|
match const_eval::const_expr_to_pat(self.cx.tcx,
|
||||||
pat.span);
|
const_expr,
|
||||||
return self.to_pattern(&pat);
|
pat.id,
|
||||||
|
pat.span) {
|
||||||
|
Ok(pat) =>
|
||||||
|
return self.to_pattern(&pat),
|
||||||
|
Err(_) =>
|
||||||
|
self.cx.tcx.sess.span_bug(
|
||||||
|
pat.span, "illegal constant"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
self.cx.tcx.sess.span_bug(
|
self.cx.tcx.sess.span_bug(
|
||||||
|
|
|
@ -1152,9 +1152,10 @@ impl f32 {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn asinh(self) -> f32 {
|
pub fn asinh(self) -> f32 {
|
||||||
match self {
|
if self == NEG_INFINITY {
|
||||||
NEG_INFINITY => NEG_INFINITY,
|
NEG_INFINITY
|
||||||
x => (x + ((x * x) + 1.0).sqrt()).ln(),
|
} else {
|
||||||
|
(self + ((self * self) + 1.0).sqrt()).ln()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1023,9 +1023,10 @@ impl f64 {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn asinh(self) -> f64 {
|
pub fn asinh(self) -> f64 {
|
||||||
match self {
|
if self == NEG_INFINITY {
|
||||||
NEG_INFINITY => NEG_INFINITY,
|
NEG_INFINITY
|
||||||
x => (x + ((x * x) + 1.0).sqrt()).ln(),
|
} else {
|
||||||
|
(self + ((self * self) + 1.0).sqrt()).ln()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1304,6 +1304,31 @@ impl CodeMap {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the backtrace `subtrace` contains `suptrace` as a prefix.
|
||||||
|
pub fn more_specific_trace(&self,
|
||||||
|
mut subtrace: ExpnId,
|
||||||
|
suptrace: ExpnId)
|
||||||
|
-> bool {
|
||||||
|
loop {
|
||||||
|
if subtrace == suptrace {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stop = self.with_expn_info(subtrace, |opt_expn_info| {
|
||||||
|
if let Some(expn_info) = opt_expn_info {
|
||||||
|
subtrace = expn_info.call_site.expn_id;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if stop {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
|
pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
|
||||||
let mut expansions = self.expansions.borrow_mut();
|
let mut expansions = self.expansions.borrow_mut();
|
||||||
expansions.push(expn_info);
|
expansions.push(expn_info);
|
||||||
|
|
|
@ -33,7 +33,7 @@ use visit::Visitor;
|
||||||
use std_inject;
|
use std_inject;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||||
let expr_span = e.span;
|
let expr_span = e.span;
|
||||||
|
@ -1275,11 +1275,41 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
|
fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
|
||||||
/* this discards information in the case of macro-defining macros */
|
debug!("new_span(sp={:?})", sp);
|
||||||
Span {
|
|
||||||
lo: sp.lo,
|
if cx.codemap().more_specific_trace(sp.expn_id, cx.backtrace()) {
|
||||||
hi: sp.hi,
|
// If the span we are looking at has a backtrace that has more
|
||||||
expn_id: cx.backtrace(),
|
// detail than our current backtrace, then we keep that
|
||||||
|
// backtrace. Honestly, I have no idea if this makes sense,
|
||||||
|
// because I have no idea why we are stripping the backtrace
|
||||||
|
// below. But the reason I made this change is because, in
|
||||||
|
// deriving, we were generating attributes with a specific
|
||||||
|
// backtrace, which was essential for `#[structural_match]` to
|
||||||
|
// be properly supported, but these backtraces were being
|
||||||
|
// stripped and replaced with a null backtrace. Sort of
|
||||||
|
// unclear why this is the case. --nmatsakis
|
||||||
|
debug!("new_span: keeping trace from {:?} because it is more specific",
|
||||||
|
sp.expn_id);
|
||||||
|
sp
|
||||||
|
} else {
|
||||||
|
// This discards information in the case of macro-defining macros.
|
||||||
|
//
|
||||||
|
// The comment above was originally added in
|
||||||
|
// b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I
|
||||||
|
// *THINK* the reason we are doing this is because we want to
|
||||||
|
// replace the backtrace of the macro contents with the
|
||||||
|
// backtrace that contains the macro use. But it's pretty
|
||||||
|
// unclear to me. --nmatsakis
|
||||||
|
let sp1 = Span {
|
||||||
|
lo: sp.lo,
|
||||||
|
hi: sp.hi,
|
||||||
|
expn_id: cx.backtrace(),
|
||||||
|
};
|
||||||
|
debug!("new_span({:?}) = {:?}", sp, sp1);
|
||||||
|
if sp.expn_id.into_u32() == 0 && env::var_os("NDM").is_some() {
|
||||||
|
panic!("NDM");
|
||||||
|
}
|
||||||
|
sp1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,8 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
|
||||||
// to bootstrap fix for #5723.
|
// to bootstrap fix for #5723.
|
||||||
("issue_5723_bootstrap", "1.0.0", None, Accepted),
|
("issue_5723_bootstrap", "1.0.0", None, Accepted),
|
||||||
|
|
||||||
|
("structural_match", "1.8.0", Some(31434), Active),
|
||||||
|
|
||||||
// A way to temporarily opt out of opt in copy. This will *never* be accepted.
|
// A way to temporarily opt out of opt in copy. This will *never* be accepted.
|
||||||
("opt_out_copy", "1.0.0", None, Removed),
|
("opt_out_copy", "1.0.0", None, Removed),
|
||||||
|
|
||||||
|
@ -304,6 +306,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
|
||||||
("link_args", Normal, Ungated),
|
("link_args", Normal, Ungated),
|
||||||
("macro_escape", Normal, Ungated),
|
("macro_escape", Normal, Ungated),
|
||||||
|
|
||||||
|
// RFC #1445.
|
||||||
|
("structural_match", Whitelisted, Gated("structural_match",
|
||||||
|
"the semantics of constant patterns is \
|
||||||
|
not yet settled")),
|
||||||
|
|
||||||
// Not used any more, but we can't feature gate it
|
// Not used any more, but we can't feature gate it
|
||||||
("no_stack_check", Normal, Ungated),
|
("no_stack_check", Normal, Ungated),
|
||||||
|
|
||||||
|
@ -682,7 +689,7 @@ impl<'a> Context<'a> {
|
||||||
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
|
fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
|
||||||
let has_feature = self.has_feature(feature);
|
let has_feature = self.has_feature(feature);
|
||||||
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
|
debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", feature, span, has_feature);
|
||||||
if !has_feature {
|
if !has_feature && !self.cm.span_allows_unstable(span) {
|
||||||
emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
|
emit_feature_err(self.span_handler, feature, span, GateIssue::Language, explain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,4 +10,5 @@ crate-type = ["dylib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fmt_macros = { path = "../libfmt_macros" }
|
fmt_macros = { path = "../libfmt_macros" }
|
||||||
|
log = { path = "../liblog" }
|
||||||
syntax = { path = "../libsyntax" }
|
syntax = { path = "../libsyntax" }
|
||||||
|
|
|
@ -78,7 +78,10 @@ fn expand_derive(cx: &mut ExtCtxt,
|
||||||
mitem: &MetaItem,
|
mitem: &MetaItem,
|
||||||
annotatable: Annotatable)
|
annotatable: Annotatable)
|
||||||
-> Annotatable {
|
-> Annotatable {
|
||||||
annotatable.map_item_or(|item| {
|
debug!("expand_derive: span = {:?}", span);
|
||||||
|
debug!("expand_derive: mitem = {:?}", mitem);
|
||||||
|
debug!("expand_derive: annotatable input = {:?}", annotatable);
|
||||||
|
let annot = annotatable.map_item_or(|item| {
|
||||||
item.map(|mut item| {
|
item.map(|mut item| {
|
||||||
if mitem.value_str().is_some() {
|
if mitem.value_str().is_some() {
|
||||||
cx.span_err(mitem.span, "unexpected value in `derive`");
|
cx.span_err(mitem.span, "unexpected value in `derive`");
|
||||||
|
@ -89,6 +92,9 @@ fn expand_derive(cx: &mut ExtCtxt,
|
||||||
cx.span_warn(mitem.span, "empty trait list in `derive`");
|
cx.span_warn(mitem.span, "empty trait list in `derive`");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut found_partial_eq = false;
|
||||||
|
let mut found_eq = false;
|
||||||
|
|
||||||
for titem in traits.iter().rev() {
|
for titem in traits.iter().rev() {
|
||||||
let tname = match titem.node {
|
let tname = match titem.node {
|
||||||
MetaItemKind::Word(ref tname) => tname,
|
MetaItemKind::Word(ref tname) => tname,
|
||||||
|
@ -107,17 +113,64 @@ fn expand_derive(cx: &mut ExtCtxt,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if &tname[..] == "Eq" {
|
||||||
|
found_eq = true;
|
||||||
|
} else if &tname[..] == "PartialEq" {
|
||||||
|
found_partial_eq = true;
|
||||||
|
}
|
||||||
|
|
||||||
// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
|
// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
|
||||||
item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
|
item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
|
||||||
intern_and_get_ident(&format!("derive_{}", tname)))));
|
intern_and_get_ident(&format!("derive_{}", tname)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RFC #1445. `#[derive(PartialEq, Eq)]` adds a (trusted)
|
||||||
|
// `#[structural_match]` attribute.
|
||||||
|
if found_partial_eq && found_eq {
|
||||||
|
// This span is **very** sensitive and crucial to
|
||||||
|
// getting the stability behavior we want. What we are
|
||||||
|
// doing is marking `#[structural_match]` with the
|
||||||
|
// span of the `#[deriving(...)]` attribute (the
|
||||||
|
// entire attribute, not just the `PartialEq` or `Eq`
|
||||||
|
// part), but with the current backtrace. The current
|
||||||
|
// backtrace will contain a topmost entry that IS this
|
||||||
|
// `#[deriving(...)]` attribute and with the
|
||||||
|
// "allow-unstable" flag set to true.
|
||||||
|
//
|
||||||
|
// Note that we do NOT use the span of the `Eq`
|
||||||
|
// text itself. You might think this is
|
||||||
|
// equivalent, because the `Eq` appears within the
|
||||||
|
// `#[deriving(Eq)]` attribute, and hence we would
|
||||||
|
// inherit the "allows unstable" from the
|
||||||
|
// backtrace. But in fact this is not always the
|
||||||
|
// case. The actual source text that led to
|
||||||
|
// deriving can be `#[$attr]`, for example, where
|
||||||
|
// `$attr == deriving(Eq)`. In that case, the
|
||||||
|
// "#[structural_match]" would be considered to
|
||||||
|
// originate not from the deriving call but from
|
||||||
|
// text outside the deriving call, and hence would
|
||||||
|
// be forbidden from using unstable
|
||||||
|
// content.
|
||||||
|
//
|
||||||
|
// See tests src/run-pass/rfc1445 for
|
||||||
|
// examples. --nmatsakis
|
||||||
|
let span = Span { expn_id: cx.backtrace(), .. span };
|
||||||
|
assert!(cx.parse_sess.codemap().span_allows_unstable(span));
|
||||||
|
debug!("inserting structural_match with span {:?}", span);
|
||||||
|
let structural_match = intern_and_get_ident("structural_match");
|
||||||
|
item.attrs.push(cx.attribute(span,
|
||||||
|
cx.meta_word(span,
|
||||||
|
structural_match)));
|
||||||
|
}
|
||||||
|
|
||||||
item
|
item
|
||||||
})
|
})
|
||||||
}, |a| {
|
}, |a| {
|
||||||
cx.span_err(span, "`derive` can only be applied to items");
|
cx.span_err(span, "`derive` can only be applied to items");
|
||||||
a
|
a
|
||||||
})
|
});
|
||||||
|
debug!("expand_derive: annotatable output = {:?}", annot);
|
||||||
|
annot
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! derive_traits {
|
macro_rules! derive_traits {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#![feature(str_char)]
|
#![feature(str_char)]
|
||||||
|
|
||||||
extern crate fmt_macros;
|
extern crate fmt_macros;
|
||||||
|
#[macro_use] extern crate log;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
|
|
||||||
|
|
|
@ -24,9 +24,13 @@ fn main() { //~ ERROR compilation successful
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
//~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead
|
//~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead
|
||||||
|
//~| WARNING floating point constants cannot be used
|
||||||
|
//~| WARNING this was previously accepted
|
||||||
match [x, 1.0] {
|
match [x, 1.0] {
|
||||||
[NAN, _] => {},
|
[NAN, _] => {},
|
||||||
_ => {},
|
_ => {},
|
||||||
};
|
};
|
||||||
//~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead
|
//~^^^ WARNING unmatchable NaN in pattern, use the is_nan method in a guard instead
|
||||||
|
//~| WARNING floating point constants cannot be used
|
||||||
|
//~| WARNING this was previously accepted
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright 2012 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// Test that structural match is only permitted with a feature gate,
|
||||||
|
// and that if a feature gate is supplied, it permits the type to be
|
||||||
|
// used in a match.
|
||||||
|
|
||||||
|
// revisions: with_gate no_gate
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![deny(future_incompatible)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
#![cfg_attr(with_gate, feature(structural_match))]
|
||||||
|
|
||||||
|
#[structural_match] //[no_gate]~ ERROR semantics of constant patterns is not yet settled
|
||||||
|
struct Foo {
|
||||||
|
x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { x: 0 };
|
||||||
|
|
||||||
|
#[rustc_error]
|
||||||
|
fn main() { //[with_gate]~ ERROR compilation successful
|
||||||
|
let y = Foo { x: 1 };
|
||||||
|
match y {
|
||||||
|
FOO => { }
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2012 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![deny(future_incompatible)]
|
||||||
|
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
struct Foo {
|
||||||
|
x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { x: 0 };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let y = Foo { x: 1 };
|
||||||
|
match y {
|
||||||
|
FOO => { }
|
||||||
|
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
|
||||||
|
//~| WARNING will become a hard error
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = 0.0;
|
||||||
|
match x {
|
||||||
|
f32::INFINITY => { }
|
||||||
|
//~^ ERROR floating point constants cannot be used in patterns
|
||||||
|
//~| WARNING will become a hard error
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2012 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![deny(future_incompatible)]
|
||||||
|
|
||||||
|
#[derive(Eq)]
|
||||||
|
struct Foo {
|
||||||
|
x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Foo {
|
||||||
|
fn eq(&self, _: &Foo) -> bool {
|
||||||
|
false // ha ha sucker!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { x: 0 };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let y = Foo { x: 1 };
|
||||||
|
match y {
|
||||||
|
FOO => { }
|
||||||
|
//~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]`
|
||||||
|
//~| WARNING will become a hard error
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,15 +21,18 @@
|
||||||
|
|
||||||
const CONSTANT: u64 = 3;
|
const CONSTANT: u64 = 3;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct Struct {
|
struct Struct {
|
||||||
a: isize,
|
a: isize,
|
||||||
b: usize,
|
b: usize,
|
||||||
}
|
}
|
||||||
const STRUCT: Struct = Struct { a: 1, b: 2 };
|
const STRUCT: Struct = Struct { a: 1, b: 2 };
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct TupleStruct(u32);
|
struct TupleStruct(u32);
|
||||||
const TUPLE_STRUCT: TupleStruct = TupleStruct(4);
|
const TUPLE_STRUCT: TupleStruct = TupleStruct(4);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum Enum {
|
enum Enum {
|
||||||
Variant1(char),
|
Variant1(char),
|
||||||
Variant2 { a: u8 },
|
Variant2 { a: u8 },
|
||||||
|
|
|
@ -17,6 +17,7 @@ use empty_struct::XEmpty2 as XFoo;
|
||||||
|
|
||||||
struct Foo;
|
struct Foo;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum Bar {
|
enum Bar {
|
||||||
Var1,
|
Var1,
|
||||||
Var2,
|
Var2,
|
||||||
|
|
|
@ -18,7 +18,10 @@ use empty_struct::*;
|
||||||
|
|
||||||
struct Empty1 {}
|
struct Empty1 {}
|
||||||
struct Empty2;
|
struct Empty2;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct Empty3 {}
|
struct Empty3 {}
|
||||||
|
|
||||||
const Empty3: Empty3 = Empty3 {};
|
const Empty3: Empty3 = Empty3 {};
|
||||||
|
|
||||||
enum E {
|
enum E {
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// pretty-expanded FIXME #23616
|
|
||||||
|
|
||||||
#![feature(collections)]
|
#![feature(collections)]
|
||||||
|
|
||||||
extern crate collections;
|
extern crate collections;
|
||||||
|
|
|
@ -9,18 +9,24 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct NewBool(bool);
|
struct NewBool(bool);
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum Direction {
|
enum Direction {
|
||||||
North,
|
North,
|
||||||
East,
|
East,
|
||||||
South,
|
South,
|
||||||
West
|
West
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct Foo {
|
struct Foo {
|
||||||
bar: Option<Direction>,
|
bar: Option<Direction>,
|
||||||
baz: NewBool
|
baz: NewBool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum EnumWithStructVariants {
|
enum EnumWithStructVariants {
|
||||||
Variant1(bool),
|
Variant1(bool),
|
||||||
Variant2 {
|
Variant2 {
|
||||||
|
@ -37,7 +43,7 @@ const VARIANT2_NORTH: EnumWithStructVariants = EnumWithStructVariants::Variant2
|
||||||
dir: Direction::North };
|
dir: Direction::North };
|
||||||
|
|
||||||
pub mod glfw {
|
pub mod glfw {
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct InputState(usize);
|
pub struct InputState(usize);
|
||||||
|
|
||||||
pub const RELEASE : InputState = InputState(0);
|
pub const RELEASE : InputState = InputState(0);
|
||||||
|
@ -82,6 +88,7 @@ fn issue_14576() {
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum C { D = 3, E = 4 }
|
enum C { D = 3, E = 4 }
|
||||||
const F : C = C::D;
|
const F : C = C::D;
|
||||||
|
|
||||||
|
@ -89,6 +96,7 @@ fn issue_14576() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn issue_13731() {
|
fn issue_13731() {
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
enum A { AA(()) }
|
enum A { AA(()) }
|
||||||
const B: A = A::AA(());
|
const B: A = A::AA(());
|
||||||
|
|
||||||
|
@ -99,6 +107,7 @@ fn issue_13731() {
|
||||||
|
|
||||||
fn issue_15393() {
|
fn issue_15393() {
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
struct Flags {
|
struct Flags {
|
||||||
bits: usize
|
bits: usize
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2012 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
macro_rules! foo {
|
||||||
|
(#[$attr:meta] $x:ident) => {
|
||||||
|
#[$attr]
|
||||||
|
struct $x {
|
||||||
|
x: u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foo! { #[derive(PartialEq, Eq)] Foo }
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { x: 0 };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let y = Foo { x: 1 };
|
||||||
|
match y {
|
||||||
|
FOO => { }
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2012 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
struct Foo {
|
||||||
|
x: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
const FOO: Foo = Foo { x: 0 };
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let y = Foo { x: 1 };
|
||||||
|
match y {
|
||||||
|
FOO => { }
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue