auto merge of #12117 : nikomatsakis/rust/issue-11913-borrow-in-aliasable-loc, r=pcwalton
Repair a rather embarassingly obvious hole that I created as part of #9629. In particular, prevent `&mut` borrows of data in an aliasable location. This used to be prevented through the restrictions mechanism, but in #9629 I modified those rules incorrectly. r? @pcwalton Fixes #11913
This commit is contained in:
commit
f0e0d9e101
@ -681,7 +681,7 @@ fn encode_explicit_self(ebml_w: &mut writer::Encoder, explicit_self: ast::Explic
|
||||
|
||||
ebml_w.end_tag();
|
||||
|
||||
fn encode_mutability(ebml_w: &writer::Encoder,
|
||||
fn encode_mutability(ebml_w: &mut writer::Encoder,
|
||||
m: ast::Mutability) {
|
||||
match m {
|
||||
MutImmutable => { ebml_w.writer.write(&[ 'i' as u8 ]); }
|
||||
|
@ -22,7 +22,6 @@ use mc = middle::mem_categorization;
|
||||
use middle::borrowck::*;
|
||||
use middle::moves;
|
||||
use middle::ty;
|
||||
use syntax::ast::{MutImmutable, MutMutable};
|
||||
use syntax::ast;
|
||||
use syntax::ast_map;
|
||||
use syntax::ast_util;
|
||||
@ -220,9 +219,8 @@ impl<'a> CheckLoanCtxt<'a> {
|
||||
|
||||
// Restrictions that would cause the new loan to be illegal:
|
||||
let illegal_if = match loan2.mutbl {
|
||||
MutableMutability => RESTR_ALIAS | RESTR_FREEZE | RESTR_CLAIM,
|
||||
ImmutableMutability => RESTR_ALIAS | RESTR_FREEZE,
|
||||
ConstMutability => RESTR_ALIAS,
|
||||
MutableMutability => RESTR_FREEZE | RESTR_CLAIM,
|
||||
ImmutableMutability => RESTR_FREEZE,
|
||||
};
|
||||
debug!("illegal_if={:?}", illegal_if);
|
||||
|
||||
@ -424,7 +422,7 @@ impl<'a> CheckLoanCtxt<'a> {
|
||||
debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})",
|
||||
cmt.repr(this.tcx()), guarantor.repr(this.tcx()));
|
||||
match guarantor.cat {
|
||||
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) => {
|
||||
mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => {
|
||||
// Statically prohibit writes to `&mut` when aliasable
|
||||
|
||||
check_for_aliasability_violation(this, expr, b);
|
||||
@ -438,43 +436,18 @@ impl<'a> CheckLoanCtxt<'a> {
|
||||
|
||||
fn check_for_aliasability_violation(this: &CheckLoanCtxt,
|
||||
expr: &ast::Expr,
|
||||
cmt: mc::cmt) -> bool {
|
||||
let mut cmt = cmt;
|
||||
|
||||
loop {
|
||||
match cmt.cat {
|
||||
mc::cat_deref(b, _, mc::region_ptr(MutMutable, _)) |
|
||||
mc::cat_downcast(b) |
|
||||
mc::cat_stack_upvar(b) |
|
||||
mc::cat_deref(b, _, mc::uniq_ptr) |
|
||||
mc::cat_interior(b, _) |
|
||||
mc::cat_discr(b, _) => {
|
||||
// Aliasability depends on base cmt
|
||||
cmt = b;
|
||||
}
|
||||
|
||||
mc::cat_copied_upvar(_) |
|
||||
mc::cat_rvalue(..) |
|
||||
mc::cat_local(..) |
|
||||
mc::cat_arg(_) |
|
||||
mc::cat_deref(_, _, mc::unsafe_ptr(..)) |
|
||||
mc::cat_static_item(..) |
|
||||
mc::cat_deref(_, _, mc::gc_ptr) |
|
||||
mc::cat_deref(_, _, mc::region_ptr(MutImmutable, _)) => {
|
||||
// Aliasability is independent of base cmt
|
||||
match cmt.freely_aliasable() {
|
||||
None => {
|
||||
return true;
|
||||
}
|
||||
Some(cause) => {
|
||||
this.bccx.report_aliasability_violation(
|
||||
expr.span,
|
||||
MutabilityViolation,
|
||||
cause);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
cmt: mc::cmt)
|
||||
-> bool {
|
||||
match cmt.freely_aliasable() {
|
||||
None => {
|
||||
return true;
|
||||
}
|
||||
Some(cause) => {
|
||||
this.bccx.report_aliasability_violation(
|
||||
expr.span,
|
||||
MutabilityViolation,
|
||||
cause);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -598,8 +571,7 @@ impl<'a> CheckLoanCtxt<'a> {
|
||||
|
||||
// Check for a non-const loan of `loan_path`
|
||||
let cont = this.each_in_scope_loan(expr.id, |loan| {
|
||||
if loan.loan_path == loan_path &&
|
||||
loan.mutbl != ConstMutability {
|
||||
if loan.loan_path == loan_path {
|
||||
this.report_illegal_mutation(expr,
|
||||
full_loan_path,
|
||||
loan);
|
||||
|
@ -151,14 +151,13 @@ that the value `(*x).f` may be mutated via the newly created reference
|
||||
restrictions `RS` that accompany the loan.
|
||||
|
||||
The first restriction `((*x).f, [MUTATE, CLAIM, FREEZE])` states that
|
||||
the lender may not mutate nor freeze `(*x).f`. Mutation is illegal
|
||||
because `(*x).f` is only supposed to be mutated via the new reference,
|
||||
not by mutating the original path `(*x).f`. Freezing is
|
||||
the lender may not mutate, freeze, nor alias `(*x).f`. Mutation is
|
||||
illegal because `(*x).f` is only supposed to be mutated via the new
|
||||
reference, not by mutating the original path `(*x).f`. Freezing is
|
||||
illegal because the path now has an `&mut` alias; so even if we the
|
||||
lender were to consider `(*x).f` to be immutable, it might be mutated
|
||||
via this alias. Both of these restrictions are temporary. They will be
|
||||
enforced for the lifetime `'a` of the loan. After the loan expires,
|
||||
the restrictions no longer apply.
|
||||
via this alias. They will be enforced for the lifetime `'a` of the
|
||||
loan. After the loan expires, the restrictions no longer apply.
|
||||
|
||||
The second restriction on `*x` is interesting because it does not
|
||||
apply to the path that was lent (`(*x).f`) but rather to a prefix of
|
||||
@ -188,11 +187,9 @@ The kinds of expressions which in-scope loans can render illegal are:
|
||||
against mutating `lv`;
|
||||
- *moves*: illegal if there is any in-scope restriction on `lv` at all;
|
||||
- *mutable borrows* (`&mut lv`): illegal there is an in-scope restriction
|
||||
against mutating `lv` or aliasing `lv`;
|
||||
against claiming `lv`;
|
||||
- *immutable borrows* (`&lv`): illegal there is an in-scope restriction
|
||||
against freezing `lv` or aliasing `lv`;
|
||||
- *read-only borrows* (`&const lv`): illegal there is an in-scope restriction
|
||||
against aliasing `lv`.
|
||||
against freezing `lv`.
|
||||
|
||||
## Formal rules
|
||||
|
||||
@ -238,19 +235,23 @@ live. (This is done via restrictions, read on.)
|
||||
We start with the `gather_loans` pass, which walks the AST looking for
|
||||
borrows. For each borrow, there are three bits of information: the
|
||||
lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
|
||||
of the resulting pointer. Given those, `gather_loans` applies three
|
||||
of the resulting pointer. Given those, `gather_loans` applies four
|
||||
validity tests:
|
||||
|
||||
1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
|
||||
compatible with the mutability of `LV` (i.e., not borrowing immutable
|
||||
data as mutable).
|
||||
|
||||
2. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
|
||||
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
|
||||
compatible with the aliasability of `LV`. The goal is to prevent
|
||||
`&mut` borrows of aliasability data.
|
||||
|
||||
3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
|
||||
the lifetime of the value being borrowed. This pass is also
|
||||
responsible for inserting root annotations to keep managed values
|
||||
alive.
|
||||
|
||||
3. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
|
||||
4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
|
||||
restrictions to maintain memory safety. These are the restrictions
|
||||
that will go into the final loan. We'll discuss in more detail below.
|
||||
|
||||
@ -313,6 +314,47 @@ be borrowed if MQ is immutable or const:
|
||||
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
|
||||
TYPE(LV) = &mut Ty
|
||||
|
||||
## Checking aliasability
|
||||
|
||||
The goal of the aliasability check is to ensure that we never permit
|
||||
`&mut` borrows of aliasable data. Formally we define a predicate
|
||||
`ALIASABLE(LV, MQ)` which if defined means that
|
||||
"borrowing `LV` with mutability `MQ` is ok". The
|
||||
Rust code corresponding to this predicate is the function
|
||||
`check_aliasability()` in `middle::borrowck::gather_loans`.
|
||||
|
||||
### Checking aliasability of variables
|
||||
|
||||
Local variables are never aliasable as they are accessible only within
|
||||
the stack frame.
|
||||
|
||||
ALIASABLE(X, MQ) // M-Var-Mut
|
||||
|
||||
### Checking aliasable of owned content
|
||||
|
||||
Owned content is aliasable if it is found in an aliasable location:
|
||||
|
||||
ALIASABLE(LV.f, MQ) // M-Field
|
||||
ALIASABLE(LV, MQ)
|
||||
|
||||
ALIASABLE(*LV, MQ) // M-Deref-Unique
|
||||
ALIASABLE(LV, MQ)
|
||||
|
||||
### Checking mutability of immutable pointer types
|
||||
|
||||
Immutable pointer types like `&T` are aliasable, and hence can only be
|
||||
borrowed immutably:
|
||||
|
||||
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
|
||||
TYPE(LV) = &Ty
|
||||
|
||||
### Checking mutability of mutable pointer types
|
||||
|
||||
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
|
||||
|
||||
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
|
||||
TYPE(LV) = &mut Ty
|
||||
|
||||
## Checking lifetime
|
||||
|
||||
These rules aim to ensure that no data is borrowed for a scope that exceeds
|
||||
|
@ -460,6 +460,11 @@ impl<'a> GatherLoanCtxt<'a> {
|
||||
return; // reported an error, no sense in reporting more.
|
||||
}
|
||||
|
||||
// Check that we don't allow mutable borrows of aliasable data.
|
||||
if check_aliasability(self.bccx, borrow_span, cmt, req_mutbl).is_err() {
|
||||
return; // reported an error, no sense in reporting more.
|
||||
}
|
||||
|
||||
// Compute the restrictions that are required to enforce the
|
||||
// loan is safe.
|
||||
let restr = restrictions::compute_restrictions(
|
||||
@ -568,11 +573,6 @@ impl<'a> GatherLoanCtxt<'a> {
|
||||
//! Implements the M-* rules in doc.rs.
|
||||
|
||||
match req_mutbl {
|
||||
ConstMutability => {
|
||||
// Data of any mutability can be lent as const.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
ImmutableMutability => {
|
||||
// both imm and mut data can be lent as imm;
|
||||
// for mutable data, this is a freeze
|
||||
@ -591,16 +591,53 @@ impl<'a> GatherLoanCtxt<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_aliasability(bccx: &BorrowckCtxt,
|
||||
borrow_span: Span,
|
||||
cmt: mc::cmt,
|
||||
req_mutbl: LoanMutability) -> Result<(),()> {
|
||||
//! Implements the A-* rules in doc.rs.
|
||||
|
||||
match req_mutbl {
|
||||
ImmutableMutability => {
|
||||
// both imm and mut data can be lent as imm;
|
||||
// for mutable data, this is a freeze
|
||||
Ok(())
|
||||
}
|
||||
|
||||
MutableMutability => {
|
||||
// Check for those cases where we cannot control
|
||||
// the aliasing and make sure that we are not
|
||||
// being asked to.
|
||||
match cmt.freely_aliasable() {
|
||||
None => {
|
||||
Ok(())
|
||||
}
|
||||
Some(mc::AliasableStaticMut) => {
|
||||
// This is nasty, but we ignore the
|
||||
// aliasing rules if the data is based in
|
||||
// a `static mut`, since those are always
|
||||
// unsafe. At your own peril and all that.
|
||||
Ok(())
|
||||
}
|
||||
Some(cause) => {
|
||||
bccx.report_aliasability_violation(
|
||||
borrow_span,
|
||||
BorrowViolation,
|
||||
cause);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn restriction_set(&self, req_mutbl: LoanMutability)
|
||||
-> RestrictionSet {
|
||||
match req_mutbl {
|
||||
ConstMutability => RESTR_EMPTY,
|
||||
ImmutableMutability => RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM,
|
||||
MutableMutability => {
|
||||
RESTR_EMPTY | RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE
|
||||
}
|
||||
ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM,
|
||||
MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,16 +53,6 @@ impl<'a> RestrictionsContext<'a> {
|
||||
fn restrict(&self,
|
||||
cmt: mc::cmt,
|
||||
restrictions: RestrictionSet) -> RestrictionResult {
|
||||
|
||||
// Check for those cases where we cannot control the aliasing
|
||||
// and make sure that we are not being asked to.
|
||||
match cmt.freely_aliasable() {
|
||||
None => {}
|
||||
Some(cause) => {
|
||||
self.check_aliasing_permitted(cause, restrictions);
|
||||
}
|
||||
}
|
||||
|
||||
match cmt.cat {
|
||||
mc::cat_rvalue(..) => {
|
||||
// Effectively, rvalues are stored into a
|
||||
@ -179,28 +169,4 @@ impl<'a> RestrictionsContext<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_aliasing_permitted(&self,
|
||||
cause: mc::AliasableReason,
|
||||
restrictions: RestrictionSet) {
|
||||
//! This method is invoked when the current `cmt` is something
|
||||
//! where aliasing cannot be controlled. It reports an error if
|
||||
//! the restrictions required that it not be aliased; currently
|
||||
//! this only occurs when re-borrowing an `&mut` pointer.
|
||||
//!
|
||||
//! NB: To be 100% consistent, we should report an error if
|
||||
//! RESTR_FREEZE is found, because we cannot prevent freezing,
|
||||
//! nor would we want to. However, we do not report such an
|
||||
//! error, because this restriction only occurs when the user
|
||||
//! is creating an `&mut` pointer to immutable or read-only
|
||||
//! data, and there is already another piece of code that
|
||||
//! checks for this condition.
|
||||
|
||||
if restrictions.intersects(RESTR_ALIAS) {
|
||||
self.bccx.report_aliasability_violation(
|
||||
self.span,
|
||||
BorrowViolation,
|
||||
cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,7 +214,6 @@ pub enum PartialTotal {
|
||||
#[deriving(Clone, Eq)]
|
||||
pub enum LoanMutability {
|
||||
ImmutableMutability,
|
||||
ConstMutability,
|
||||
MutableMutability,
|
||||
}
|
||||
|
||||
@ -232,7 +231,6 @@ impl ToStr for LoanMutability {
|
||||
fn to_str(&self) -> ~str {
|
||||
match *self {
|
||||
ImmutableMutability => ~"immutable",
|
||||
ConstMutability => ~"read-only",
|
||||
MutableMutability => ~"mutable",
|
||||
}
|
||||
}
|
||||
@ -318,7 +316,6 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
|
||||
// - `RESTR_MUTATE`: The lvalue may not be modified.
|
||||
// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden.
|
||||
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
|
||||
// - `RESTR_ALIAS`: All borrows of the lvalue are forbidden.
|
||||
//
|
||||
// In addition, no value which is restricted may be moved. Therefore,
|
||||
// restrictions are meaningful even if the RestrictionSet is empty,
|
||||
@ -338,7 +335,6 @@ pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000};
|
||||
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
|
||||
pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010};
|
||||
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100};
|
||||
pub static RESTR_ALIAS: RestrictionSet = RestrictionSet {bits: 0b1000};
|
||||
|
||||
impl RestrictionSet {
|
||||
pub fn intersects(&self, restr: RestrictionSet) -> bool {
|
||||
@ -663,8 +659,8 @@ impl BorrowckCtxt {
|
||||
kind: AliasableViolationKind,
|
||||
cause: mc::AliasableReason) {
|
||||
let prefix = match kind {
|
||||
MutabilityViolation => "cannot assign to an `&mut`",
|
||||
BorrowViolation => "cannot borrow an `&mut`"
|
||||
MutabilityViolation => "cannot assign to data",
|
||||
BorrowViolation => "cannot borrow data mutably"
|
||||
};
|
||||
|
||||
match cause {
|
||||
@ -673,17 +669,21 @@ impl BorrowckCtxt {
|
||||
span,
|
||||
format!("{} in an aliasable location", prefix));
|
||||
}
|
||||
mc::AliasableManaged => {
|
||||
self.tcx.sess.span_err(span, format!("{} in a `@` pointer",
|
||||
prefix))
|
||||
}
|
||||
mc::AliasableBorrowed(m) => {
|
||||
mc::AliasableStatic |
|
||||
mc::AliasableStaticMut => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
format!("{} in a `&{}` pointer; \
|
||||
try an `&mut` instead",
|
||||
prefix,
|
||||
self.mut_to_keyword(m)));
|
||||
format!("{} in a static location", prefix));
|
||||
}
|
||||
mc::AliasableManaged => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
format!("{} in a `@` pointer", prefix));
|
||||
}
|
||||
mc::AliasableBorrowed(_) => {
|
||||
self.tcx.sess.span_err(
|
||||
span,
|
||||
format!("{} in a `&` reference", prefix));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1100,7 +1100,9 @@ pub fn field_mutbl(tcx: ty::ctxt,
|
||||
pub enum AliasableReason {
|
||||
AliasableManaged,
|
||||
AliasableBorrowed(ast::Mutability),
|
||||
AliasableOther
|
||||
AliasableOther,
|
||||
AliasableStatic,
|
||||
AliasableStaticMut,
|
||||
}
|
||||
|
||||
impl cmt_ {
|
||||
@ -1130,10 +1132,6 @@ impl cmt_ {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_freely_aliasable(&self) -> bool {
|
||||
self.freely_aliasable().is_some()
|
||||
}
|
||||
|
||||
pub fn freely_aliasable(&self) -> Option<AliasableReason> {
|
||||
/*!
|
||||
* Returns `Some(_)` if this lvalue represents a freely aliasable
|
||||
@ -1145,20 +1143,36 @@ impl cmt_ {
|
||||
// aliased and eventually recused.
|
||||
|
||||
match self.cat {
|
||||
cat_deref(b, _, region_ptr(MutMutable, _)) |
|
||||
cat_downcast(b) |
|
||||
cat_stack_upvar(b) |
|
||||
cat_deref(b, _, uniq_ptr) |
|
||||
cat_interior(b, _) |
|
||||
cat_discr(b, _) => {
|
||||
// Aliasability depends on base cmt
|
||||
b.freely_aliasable()
|
||||
}
|
||||
|
||||
cat_copied_upvar(CopiedUpvar {onceness: ast::Once, ..}) |
|
||||
cat_rvalue(..) |
|
||||
cat_local(..) |
|
||||
cat_arg(_) |
|
||||
cat_deref(_, _, unsafe_ptr(..)) | // of course it is aliasable, but...
|
||||
cat_deref(_, _, region_ptr(MutMutable, _)) => {
|
||||
cat_deref(_, _, unsafe_ptr(..)) => { // yes, it's aliasable, but...
|
||||
None
|
||||
}
|
||||
|
||||
cat_copied_upvar(CopiedUpvar {onceness: ast::Many, ..}) |
|
||||
cat_static_item(..) => {
|
||||
cat_copied_upvar(CopiedUpvar {onceness: ast::Many, ..}) => {
|
||||
Some(AliasableOther)
|
||||
}
|
||||
|
||||
cat_static_item(..) => {
|
||||
if self.mutbl.is_mutable() {
|
||||
Some(AliasableStaticMut)
|
||||
} else {
|
||||
Some(AliasableStatic)
|
||||
}
|
||||
}
|
||||
|
||||
cat_deref(_, _, gc_ptr) => {
|
||||
Some(AliasableManaged)
|
||||
}
|
||||
@ -1166,14 +1180,6 @@ impl cmt_ {
|
||||
cat_deref(_, _, region_ptr(m @ MutImmutable, _)) => {
|
||||
Some(AliasableBorrowed(m))
|
||||
}
|
||||
|
||||
cat_downcast(..) |
|
||||
cat_stack_upvar(..) |
|
||||
cat_deref(_, _, uniq_ptr) |
|
||||
cat_interior(..) |
|
||||
cat_discr(..) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "clone", "Clone"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -39,7 +38,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
pub fn expand_deriving_deep_clone(cx: &mut ExtCtxt,
|
||||
@ -48,8 +47,7 @@ pub fn expand_deriving_deep_clone(cx: &mut ExtCtxt,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "clone", "DeepClone"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -69,7 +67,7 @@ pub fn expand_deriving_deep_clone(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn cs_clone(
|
||||
|
@ -45,8 +45,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
|
||||
);
|
||||
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "cmp", "Eq"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -55,5 +54,5 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt,
|
||||
md!("ne", cs_ne)
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
@ -35,8 +35,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
||||
);
|
||||
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "cmp", "Ord"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -47,7 +46,7 @@ pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
||||
md!("ge", false, true)
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
/// Strict inequality.
|
||||
|
@ -24,8 +24,7 @@ pub fn expand_deriving_totaleq(cx: &mut ExtCtxt,
|
||||
}
|
||||
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "cmp", "TotalEq"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -42,5 +41,5 @@ pub fn expand_deriving_totaleq(cx: &mut ExtCtxt,
|
||||
}
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
@ -21,8 +21,7 @@ pub fn expand_deriving_totalord(cx: &mut ExtCtxt,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item]) -> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "cmp", "TotalOrd"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -40,7 +39,7 @@ pub fn expand_deriving_totalord(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,8 +26,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item]) -> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new_(~["serialize", "Decodable"], None,
|
||||
~[~Literal(Path::new_local("__D"))], true),
|
||||
additional_bounds: ~[],
|
||||
@ -50,7 +49,7 @@ pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
|
||||
|
@ -18,10 +18,9 @@ pub fn expand_deriving_default(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "default", "Default"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -38,7 +37,7 @@ pub fn expand_deriving_default(cx: &mut ExtCtxt,
|
||||
},
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
|
||||
|
@ -87,8 +87,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item]) -> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new_(~["serialize", "Encodable"], None,
|
||||
~[~Literal(Path::new_local("__E"))], true),
|
||||
additional_bounds: ~[],
|
||||
@ -111,7 +110,7 @@ pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
|
||||
|
@ -193,13 +193,12 @@ pub use self::ty::*;
|
||||
mod ty;
|
||||
|
||||
pub struct TraitDef<'a> {
|
||||
/// The extension context
|
||||
cx: &'a mut ExtCtxt<'a>,
|
||||
/// The span for the current #[deriving(Foo)] header.
|
||||
span: Span,
|
||||
|
||||
/// Path of the trait, including any type parameters
|
||||
path: Path<'a>,
|
||||
|
||||
/// Additional bounds required of any type parameters of the type,
|
||||
/// other than the current trait
|
||||
additional_bounds: ~[Ty<'a>],
|
||||
@ -321,6 +320,7 @@ pub type EnumNonMatchFunc<'a> =
|
||||
|
||||
impl<'a> TraitDef<'a> {
|
||||
pub fn expand(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
_mitem: @ast::MetaItem,
|
||||
in_items: ~[@ast::Item]) -> ~[@ast::Item] {
|
||||
let mut result = ~[];
|
||||
@ -328,12 +328,14 @@ impl<'a> TraitDef<'a> {
|
||||
result.push(*item);
|
||||
match item.node {
|
||||
ast::ItemStruct(struct_def, ref generics) => {
|
||||
result.push(self.expand_struct_def(struct_def,
|
||||
result.push(self.expand_struct_def(cx,
|
||||
struct_def,
|
||||
item.ident,
|
||||
generics));
|
||||
}
|
||||
ast::ItemEnum(ref enum_def, ref generics) => {
|
||||
result.push(self.expand_enum_def(enum_def,
|
||||
result.push(self.expand_enum_def(cx,
|
||||
enum_def,
|
||||
item.ident,
|
||||
generics));
|
||||
}
|
||||
@ -354,9 +356,9 @@ impl<'a> TraitDef<'a> {
|
||||
*
|
||||
*/
|
||||
fn create_derived_impl(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
type_ident: Ident, generics: &Generics,
|
||||
methods: ~[@ast::Method]) -> @ast::Item {
|
||||
let cx = &*self.cx;
|
||||
let trait_path = self.path.to_path(cx, self.span, type_ident, generics);
|
||||
|
||||
let mut trait_generics = self.generics.to_generics(cx, self.span,
|
||||
@ -372,7 +374,8 @@ impl<'a> TraitDef<'a> {
|
||||
let mut bounds = opt_vec::from(
|
||||
// extra restrictions on the generics parameters to the type being derived upon
|
||||
self.additional_bounds.map(|p| {
|
||||
cx.typarambound(p.to_path(cx, self.span, type_ident, generics))
|
||||
cx.typarambound(p.to_path(cx, self.span,
|
||||
type_ident, generics))
|
||||
}));
|
||||
// require the current trait
|
||||
bounds.push(cx.typarambound(trait_path.clone()));
|
||||
@ -393,15 +396,15 @@ impl<'a> TraitDef<'a> {
|
||||
// Create the type of `self`.
|
||||
let self_type = cx.ty_path(
|
||||
cx.path_all(self.span, false, ~[ type_ident ], self_lifetimes,
|
||||
opt_vec::take_vec(self_ty_params)), None);
|
||||
opt_vec::take_vec(self_ty_params)), None);
|
||||
|
||||
let doc_attr = cx.attribute(
|
||||
self.span,
|
||||
cx.meta_name_value(self.span,
|
||||
InternedString::new("doc"),
|
||||
ast::LitStr(token::intern_and_get_ident(
|
||||
"Automatically derived."),
|
||||
ast::CookedStr)));
|
||||
"Automatically derived."),
|
||||
ast::CookedStr)));
|
||||
cx.item(
|
||||
self.span,
|
||||
::parse::token::special_idents::clownshoes_extensions,
|
||||
@ -411,68 +414,77 @@ impl<'a> TraitDef<'a> {
|
||||
}
|
||||
|
||||
fn expand_struct_def(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
struct_def: &StructDef,
|
||||
type_ident: Ident,
|
||||
generics: &Generics) -> @ast::Item {
|
||||
let methods = self.methods.map(|method_def| {
|
||||
let (explicit_self, self_args, nonself_args, tys) =
|
||||
method_def.split_self_nonself_args(self, type_ident, generics);
|
||||
method_def.split_self_nonself_args(
|
||||
cx, self, type_ident, generics);
|
||||
|
||||
let body = if method_def.is_static() {
|
||||
method_def.expand_static_struct_method_body(
|
||||
cx,
|
||||
self,
|
||||
struct_def,
|
||||
type_ident,
|
||||
self_args, nonself_args)
|
||||
} else {
|
||||
method_def.expand_struct_method_body(self,
|
||||
method_def.expand_struct_method_body(cx,
|
||||
self,
|
||||
struct_def,
|
||||
type_ident,
|
||||
self_args, nonself_args)
|
||||
};
|
||||
|
||||
method_def.create_method(self,
|
||||
method_def.create_method(cx, self,
|
||||
type_ident, generics,
|
||||
explicit_self, tys,
|
||||
body)
|
||||
});
|
||||
|
||||
self.create_derived_impl(type_ident, generics, methods)
|
||||
self.create_derived_impl(cx, type_ident, generics, methods)
|
||||
}
|
||||
|
||||
fn expand_enum_def(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
enum_def: &EnumDef,
|
||||
type_ident: Ident,
|
||||
generics: &Generics) -> @ast::Item {
|
||||
let methods = self.methods.map(|method_def| {
|
||||
let (explicit_self, self_args, nonself_args, tys) =
|
||||
method_def.split_self_nonself_args(self, type_ident, generics);
|
||||
method_def.split_self_nonself_args(cx, self,
|
||||
type_ident, generics);
|
||||
|
||||
let body = if method_def.is_static() {
|
||||
method_def.expand_static_enum_method_body(
|
||||
cx,
|
||||
self,
|
||||
enum_def,
|
||||
type_ident,
|
||||
self_args, nonself_args)
|
||||
} else {
|
||||
method_def.expand_enum_method_body(self,
|
||||
method_def.expand_enum_method_body(cx,
|
||||
self,
|
||||
enum_def,
|
||||
type_ident,
|
||||
self_args, nonself_args)
|
||||
};
|
||||
|
||||
method_def.create_method(self,
|
||||
method_def.create_method(cx, self,
|
||||
type_ident, generics,
|
||||
explicit_self, tys,
|
||||
body)
|
||||
});
|
||||
|
||||
self.create_derived_impl(type_ident, generics, methods)
|
||||
self.create_derived_impl(cx, type_ident, generics, methods)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MethodDef<'a> {
|
||||
fn call_substructure_method(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
type_ident: Ident,
|
||||
self_args: &[@Expr],
|
||||
@ -481,26 +493,33 @@ impl<'a> MethodDef<'a> {
|
||||
-> @Expr {
|
||||
let substructure = Substructure {
|
||||
type_ident: type_ident,
|
||||
method_ident: trait_.cx.ident_of(self.name),
|
||||
method_ident: cx.ident_of(self.name),
|
||||
self_args: self_args,
|
||||
nonself_args: nonself_args,
|
||||
fields: fields
|
||||
};
|
||||
(self.combine_substructure)(trait_.cx, trait_.span,
|
||||
(self.combine_substructure)(cx, trait_.span,
|
||||
&substructure)
|
||||
}
|
||||
|
||||
fn get_ret_ty(&self, trait_: &TraitDef,
|
||||
generics: &Generics, type_ident: Ident) -> P<ast::Ty> {
|
||||
self.ret_ty.to_ty(trait_.cx, trait_.span, type_ident, generics)
|
||||
fn get_ret_ty(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
generics: &Generics,
|
||||
type_ident: Ident)
|
||||
-> P<ast::Ty> {
|
||||
self.ret_ty.to_ty(cx, trait_.span, type_ident, generics)
|
||||
}
|
||||
|
||||
fn is_static(&self) -> bool {
|
||||
self.explicit_self.is_none()
|
||||
}
|
||||
|
||||
fn split_self_nonself_args(&self, trait_: &TraitDef,
|
||||
type_ident: Ident, generics: &Generics)
|
||||
fn split_self_nonself_args(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
type_ident: Ident,
|
||||
generics: &Generics)
|
||||
-> (ast::ExplicitSelf, ~[@Expr], ~[@Expr], ~[(Ident, P<ast::Ty>)]) {
|
||||
|
||||
let mut self_args = ~[];
|
||||
@ -511,7 +530,7 @@ impl<'a> MethodDef<'a> {
|
||||
let ast_explicit_self = match self.explicit_self {
|
||||
Some(ref self_ptr) => {
|
||||
let (self_expr, explicit_self) =
|
||||
ty::get_explicit_self(trait_.cx, trait_.span, self_ptr);
|
||||
ty::get_explicit_self(cx, trait_.span, self_ptr);
|
||||
|
||||
self_args.push(self_expr);
|
||||
nonstatic = true;
|
||||
@ -522,11 +541,11 @@ impl<'a> MethodDef<'a> {
|
||||
};
|
||||
|
||||
for (i, ty) in self.args.iter().enumerate() {
|
||||
let ast_ty = ty.to_ty(trait_.cx, trait_.span, type_ident, generics);
|
||||
let ident = trait_.cx.ident_of(format!("__arg_{}", i));
|
||||
let ast_ty = ty.to_ty(cx, trait_.span, type_ident, generics);
|
||||
let ident = cx.ident_of(format!("__arg_{}", i));
|
||||
arg_tys.push((ident, ast_ty));
|
||||
|
||||
let arg_expr = trait_.cx.expr_ident(trait_.span, ident);
|
||||
let arg_expr = cx.expr_ident(trait_.span, ident);
|
||||
|
||||
match *ty {
|
||||
// for static methods, just treat any Self
|
||||
@ -535,7 +554,7 @@ impl<'a> MethodDef<'a> {
|
||||
self_args.push(arg_expr);
|
||||
}
|
||||
Ptr(~Self, _) if nonstatic => {
|
||||
self_args.push(trait_.cx.expr_deref(trait_.span, arg_expr))
|
||||
self_args.push(cx.expr_deref(trait_.span, arg_expr))
|
||||
}
|
||||
_ => {
|
||||
nonself_args.push(arg_expr);
|
||||
@ -546,35 +565,37 @@ impl<'a> MethodDef<'a> {
|
||||
(ast_explicit_self, self_args, nonself_args, arg_tys)
|
||||
}
|
||||
|
||||
fn create_method(&self, trait_: &TraitDef,
|
||||
fn create_method(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
type_ident: Ident,
|
||||
generics: &Generics,
|
||||
explicit_self: ast::ExplicitSelf,
|
||||
arg_types: ~[(Ident, P<ast::Ty>)],
|
||||
body: @Expr) -> @ast::Method {
|
||||
// create the generics that aren't for Self
|
||||
let fn_generics = self.generics.to_generics(trait_.cx, trait_.span, type_ident, generics);
|
||||
let fn_generics = self.generics.to_generics(cx, trait_.span, type_ident, generics);
|
||||
|
||||
let self_arg = match explicit_self.node {
|
||||
ast::SelfStatic => None,
|
||||
_ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable))
|
||||
};
|
||||
let args = arg_types.move_iter().map(|(name, ty)| {
|
||||
trait_.cx.arg(trait_.span, name, ty)
|
||||
cx.arg(trait_.span, name, ty)
|
||||
});
|
||||
let args = self_arg.move_iter().chain(args).collect();
|
||||
|
||||
let ret_type = self.get_ret_ty(trait_, generics, type_ident);
|
||||
let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident);
|
||||
|
||||
let method_ident = trait_.cx.ident_of(self.name);
|
||||
let fn_decl = trait_.cx.fn_decl(args, ret_type);
|
||||
let body_block = trait_.cx.block_expr(body);
|
||||
let method_ident = cx.ident_of(self.name);
|
||||
let fn_decl = cx.fn_decl(args, ret_type);
|
||||
let body_block = cx.block_expr(body);
|
||||
|
||||
let attrs = if self.inline {
|
||||
~[
|
||||
trait_.cx
|
||||
cx
|
||||
.attribute(trait_.span,
|
||||
trait_.cx
|
||||
cx
|
||||
.meta_word(trait_.span,
|
||||
InternedString::new(
|
||||
"inline")))
|
||||
@ -620,6 +641,7 @@ impl<'a> MethodDef<'a> {
|
||||
~~~
|
||||
*/
|
||||
fn expand_struct_method_body(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
struct_def: &StructDef,
|
||||
type_ident: Ident,
|
||||
@ -631,7 +653,7 @@ impl<'a> MethodDef<'a> {
|
||||
// [fields of next Self arg], [etc]]
|
||||
let mut patterns = ~[];
|
||||
for i in range(0u, self_args.len()) {
|
||||
let (pat, ident_expr) = trait_.create_struct_pattern(type_ident, struct_def,
|
||||
let (pat, ident_expr) = trait_.create_struct_pattern(cx, type_ident, struct_def,
|
||||
format!("__self_{}", i),
|
||||
ast::MutImmutable);
|
||||
patterns.push(pat);
|
||||
@ -655,13 +677,14 @@ impl<'a> MethodDef<'a> {
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
[] => { trait_.cx.span_bug(trait_.span,
|
||||
"no self arguments to non-static method \
|
||||
in generic `deriving`") }
|
||||
[] => { cx.span_bug(trait_.span,
|
||||
"no self arguments to non-static method \
|
||||
in generic `deriving`") }
|
||||
};
|
||||
|
||||
// body of the inner most destructuring match
|
||||
let mut body = self.call_substructure_method(
|
||||
cx,
|
||||
trait_,
|
||||
type_ident,
|
||||
self_args,
|
||||
@ -672,22 +695,24 @@ impl<'a> MethodDef<'a> {
|
||||
// structs. This is actually right-to-left, but it shoudn't
|
||||
// matter.
|
||||
for (&arg_expr, &pat) in self_args.iter().zip(patterns.iter()) {
|
||||
body = trait_.cx.expr_match(trait_.span, arg_expr,
|
||||
~[ trait_.cx.arm(trait_.span, ~[pat], body) ])
|
||||
body = cx.expr_match(trait_.span, arg_expr,
|
||||
~[ cx.arm(trait_.span, ~[pat], body) ])
|
||||
}
|
||||
body
|
||||
}
|
||||
|
||||
fn expand_static_struct_method_body(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
struct_def: &StructDef,
|
||||
type_ident: Ident,
|
||||
self_args: &[@Expr],
|
||||
nonself_args: &[@Expr])
|
||||
-> @Expr {
|
||||
let summary = trait_.summarise_struct(struct_def);
|
||||
let summary = trait_.summarise_struct(cx, struct_def);
|
||||
|
||||
self.call_substructure_method(trait_,
|
||||
self.call_substructure_method(cx,
|
||||
trait_,
|
||||
type_ident,
|
||||
self_args, nonself_args,
|
||||
&StaticStruct(struct_def, summary))
|
||||
@ -720,14 +745,15 @@ impl<'a> MethodDef<'a> {
|
||||
~~~
|
||||
*/
|
||||
fn expand_enum_method_body(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
enum_def: &EnumDef,
|
||||
type_ident: Ident,
|
||||
self_args: &[@Expr],
|
||||
nonself_args: &[@Expr])
|
||||
-> @Expr {
|
||||
-> @Expr {
|
||||
let mut matches = ~[];
|
||||
self.build_enum_match(trait_, enum_def, type_ident,
|
||||
self.build_enum_match(cx, trait_, enum_def, type_ident,
|
||||
self_args, nonself_args,
|
||||
None, &mut matches, 0)
|
||||
}
|
||||
@ -755,6 +781,7 @@ impl<'a> MethodDef<'a> {
|
||||
the first call).
|
||||
*/
|
||||
fn build_enum_match(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
enum_def: &EnumDef,
|
||||
type_ident: Ident,
|
||||
@ -764,13 +791,13 @@ impl<'a> MethodDef<'a> {
|
||||
matches_so_far: &mut ~[(uint, P<ast::Variant>,
|
||||
~[(Span, Option<Ident>, @Expr)])],
|
||||
match_count: uint) -> @Expr {
|
||||
let cx = &trait_.cx;
|
||||
if match_count == self_args.len() {
|
||||
// we've matched against all arguments, so make the final
|
||||
// expression at the bottom of the match tree
|
||||
if matches_so_far.len() == 0 {
|
||||
cx.span_bug(trait_.span,
|
||||
"no self match on an enum in generic `deriving`");
|
||||
"no self match on an enum in \
|
||||
generic `deriving`");
|
||||
}
|
||||
// we currently have a vec of vecs, where each
|
||||
// subvec is the fields of one of the arguments,
|
||||
@ -820,7 +847,7 @@ impl<'a> MethodDef<'a> {
|
||||
substructure = EnumNonMatching(*matches_so_far);
|
||||
}
|
||||
}
|
||||
self.call_substructure_method(trait_, type_ident,
|
||||
self.call_substructure_method(cx, trait_, type_ident,
|
||||
self_args, nonself_args,
|
||||
&substructure)
|
||||
|
||||
@ -846,12 +873,14 @@ impl<'a> MethodDef<'a> {
|
||||
|
||||
// matching-variant match
|
||||
let variant = enum_def.variants[index];
|
||||
let (pattern, idents) = trait_.create_enum_variant_pattern(variant,
|
||||
let (pattern, idents) = trait_.create_enum_variant_pattern(cx,
|
||||
variant,
|
||||
current_match_str,
|
||||
ast::MutImmutable);
|
||||
|
||||
matches_so_far.push((index, variant, idents));
|
||||
let arm_expr = self.build_enum_match(trait_,
|
||||
let arm_expr = self.build_enum_match(cx,
|
||||
trait_,
|
||||
enum_def,
|
||||
type_ident,
|
||||
self_args, nonself_args,
|
||||
@ -863,18 +892,20 @@ impl<'a> MethodDef<'a> {
|
||||
|
||||
if enum_def.variants.len() > 1 {
|
||||
let e = &EnumNonMatching(&[]);
|
||||
let wild_expr = self.call_substructure_method(trait_, type_ident,
|
||||
let wild_expr = self.call_substructure_method(cx, trait_, type_ident,
|
||||
self_args, nonself_args,
|
||||
e);
|
||||
let wild_arm = cx.arm(trait_.span,
|
||||
~[ cx.pat_wild(trait_.span) ],
|
||||
wild_expr);
|
||||
let wild_arm = cx.arm(
|
||||
trait_.span,
|
||||
~[ cx.pat_wild(trait_.span) ],
|
||||
wild_expr);
|
||||
arms.push(wild_arm);
|
||||
}
|
||||
} else {
|
||||
// create an arm matching on each variant
|
||||
for (index, &variant) in enum_def.variants.iter().enumerate() {
|
||||
let (pattern, idents) = trait_.create_enum_variant_pattern(variant,
|
||||
let (pattern, idents) = trait_.create_enum_variant_pattern(cx,
|
||||
variant,
|
||||
current_match_str,
|
||||
ast::MutImmutable);
|
||||
|
||||
@ -885,7 +916,8 @@ impl<'a> MethodDef<'a> {
|
||||
Some(i) if index == i => Some(i),
|
||||
_ => None
|
||||
};
|
||||
let arm_expr = self.build_enum_match(trait_,
|
||||
let arm_expr = self.build_enum_match(cx,
|
||||
trait_,
|
||||
enum_def,
|
||||
type_ident,
|
||||
self_args, nonself_args,
|
||||
@ -905,6 +937,7 @@ impl<'a> MethodDef<'a> {
|
||||
}
|
||||
|
||||
fn expand_static_enum_method_body(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
trait_: &TraitDef,
|
||||
enum_def: &EnumDef,
|
||||
type_ident: Ident,
|
||||
@ -915,15 +948,15 @@ impl<'a> MethodDef<'a> {
|
||||
let ident = v.node.name;
|
||||
let summary = match v.node.kind {
|
||||
ast::TupleVariantKind(ref args) => {
|
||||
Unnamed(args.map(|va| trait_.set_expn_info(va.ty.span)))
|
||||
Unnamed(args.map(|va| trait_.set_expn_info(cx, va.ty.span)))
|
||||
}
|
||||
ast::StructVariantKind(struct_def) => {
|
||||
trait_.summarise_struct(struct_def)
|
||||
trait_.summarise_struct(cx, struct_def)
|
||||
}
|
||||
};
|
||||
(ident, v.span, summary)
|
||||
});
|
||||
self.call_substructure_method(trait_, type_ident,
|
||||
self.call_substructure_method(cx, trait_, type_ident,
|
||||
self_args, nonself_args,
|
||||
&StaticEnum(enum_def, summary))
|
||||
}
|
||||
@ -936,9 +969,11 @@ enum StructType {
|
||||
|
||||
// general helper methods.
|
||||
impl<'a> TraitDef<'a> {
|
||||
fn set_expn_info(&self, mut to_set: Span) -> Span {
|
||||
fn set_expn_info(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
mut to_set: Span) -> Span {
|
||||
let trait_name = match self.path.path.last() {
|
||||
None => self.cx.span_bug(self.span, "trait with empty path in generic `deriving`"),
|
||||
None => cx.span_bug(self.span, "trait with empty path in generic `deriving`"),
|
||||
Some(name) => *name
|
||||
};
|
||||
to_set.expn_info = Some(@codemap::ExpnInfo {
|
||||
@ -952,11 +987,13 @@ impl<'a> TraitDef<'a> {
|
||||
to_set
|
||||
}
|
||||
|
||||
fn summarise_struct(&self, struct_def: &StructDef) -> StaticFields {
|
||||
fn summarise_struct(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
struct_def: &StructDef) -> StaticFields {
|
||||
let mut named_idents = ~[];
|
||||
let mut just_spans = ~[];
|
||||
for field in struct_def.fields.iter(){
|
||||
let sp = self.set_expn_info(field.span);
|
||||
let sp = self.set_expn_info(cx, field.span);
|
||||
match field.node.kind {
|
||||
ast::NamedField(ident, _) => named_idents.push((ident, sp)),
|
||||
ast::UnnamedField => just_spans.push(sp),
|
||||
@ -964,9 +1001,9 @@ impl<'a> TraitDef<'a> {
|
||||
}
|
||||
|
||||
match (just_spans.is_empty(), named_idents.is_empty()) {
|
||||
(false, false) => self.cx.span_bug(self.span,
|
||||
"a struct with named and unnamed \
|
||||
fields in generic `deriving`"),
|
||||
(false, false) => cx.span_bug(self.span,
|
||||
"a struct with named and unnamed \
|
||||
fields in generic `deriving`"),
|
||||
// named fields
|
||||
(_, false) => Named(named_idents),
|
||||
// tuple structs (includes empty structs)
|
||||
@ -975,23 +1012,23 @@ impl<'a> TraitDef<'a> {
|
||||
}
|
||||
|
||||
fn create_subpatterns(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
field_paths: ~[ast::Path],
|
||||
mutbl: ast::Mutability)
|
||||
-> ~[@ast::Pat] {
|
||||
field_paths.map(|path| {
|
||||
self.cx.pat(path.span,
|
||||
cx.pat(path.span,
|
||||
ast::PatIdent(ast::BindByRef(mutbl), (*path).clone(), None))
|
||||
})
|
||||
}
|
||||
|
||||
fn create_struct_pattern(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
struct_ident: Ident,
|
||||
struct_def: &StructDef,
|
||||
prefix: &str,
|
||||
mutbl: ast::Mutability)
|
||||
-> (@ast::Pat, ~[(Span, Option<Ident>, @Expr)]) {
|
||||
let cx = &self.cx;
|
||||
|
||||
-> (@ast::Pat, ~[(Span, Option<Ident>, @Expr)]) {
|
||||
if struct_def.fields.is_empty() {
|
||||
return (
|
||||
cx.pat_ident_binding_mode(
|
||||
@ -1006,7 +1043,7 @@ impl<'a> TraitDef<'a> {
|
||||
let mut struct_type = Unknown;
|
||||
|
||||
for (i, struct_field) in struct_def.fields.iter().enumerate() {
|
||||
let sp = self.set_expn_info(struct_field.span);
|
||||
let sp = self.set_expn_info(cx, struct_field.span);
|
||||
let opt_id = match struct_field.node.kind {
|
||||
ast::NamedField(ident, _) if (struct_type == Unknown ||
|
||||
struct_type == Record) => {
|
||||
@ -1024,11 +1061,13 @@ impl<'a> TraitDef<'a> {
|
||||
};
|
||||
let path = cx.path_ident(sp, cx.ident_of(format!("{}_{}", prefix, i)));
|
||||
paths.push(path.clone());
|
||||
let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(path))));
|
||||
let val = cx.expr(
|
||||
sp, ast::ExprParen(
|
||||
cx.expr_deref(sp, cx.expr_path(path))));
|
||||
ident_expr.push((sp, opt_id, val));
|
||||
}
|
||||
|
||||
let subpats = self.create_subpatterns(paths, mutbl);
|
||||
let subpats = self.create_subpatterns(cx, paths, mutbl);
|
||||
|
||||
// struct_type is definitely not Unknown, since struct_def.fields
|
||||
// must be nonempty to reach here
|
||||
@ -1046,17 +1085,17 @@ impl<'a> TraitDef<'a> {
|
||||
}
|
||||
|
||||
fn create_enum_variant_pattern(&self,
|
||||
cx: &mut ExtCtxt,
|
||||
variant: &ast::Variant,
|
||||
prefix: &str,
|
||||
mutbl: ast::Mutability)
|
||||
-> (@ast::Pat, ~[(Span, Option<Ident>, @Expr)]) {
|
||||
let cx = &*self.cx;
|
||||
let variant_ident = variant.node.name;
|
||||
match variant.node.kind {
|
||||
ast::TupleVariantKind(ref variant_args) => {
|
||||
if variant_args.is_empty() {
|
||||
return (cx.pat_ident_binding_mode(variant.span, variant_ident,
|
||||
ast::BindByValue(ast::MutImmutable)),
|
||||
ast::BindByValue(ast::MutImmutable)),
|
||||
~[]);
|
||||
}
|
||||
|
||||
@ -1065,21 +1104,22 @@ impl<'a> TraitDef<'a> {
|
||||
let mut paths = ~[];
|
||||
let mut ident_expr = ~[];
|
||||
for (i, va) in variant_args.iter().enumerate() {
|
||||
let sp = self.set_expn_info(va.ty.span);
|
||||
let sp = self.set_expn_info(cx, va.ty.span);
|
||||
let path = cx.path_ident(sp, cx.ident_of(format!("{}_{}", prefix, i)));
|
||||
|
||||
paths.push(path.clone());
|
||||
let val = cx.expr(sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(path))));
|
||||
let val = cx.expr(
|
||||
sp, ast::ExprParen(cx.expr_deref(sp, cx.expr_path(path))));
|
||||
ident_expr.push((sp, None, val));
|
||||
}
|
||||
|
||||
let subpats = self.create_subpatterns(paths, mutbl);
|
||||
let subpats = self.create_subpatterns(cx, paths, mutbl);
|
||||
|
||||
(cx.pat_enum(variant.span, matching_path, subpats),
|
||||
ident_expr)
|
||||
}
|
||||
ast::StructVariantKind(struct_def) => {
|
||||
self.create_struct_pattern(variant_ident, struct_def,
|
||||
self.create_struct_pattern(cx, variant_ident, struct_def,
|
||||
prefix, mutbl)
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,7 @@ pub fn expand_deriving_iter_bytes(cx: &mut ExtCtxt,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item]) -> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "to_bytes", "IterBytes"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -42,7 +41,7 @@ pub fn expand_deriving_iter_bytes(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn iter_bytes_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
|
||||
|
@ -21,8 +21,7 @@ pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item]) -> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "num", "FromPrimitive"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -62,7 +61,7 @@ pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
|
||||
]
|
||||
};
|
||||
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
|
||||
|
@ -20,10 +20,9 @@ pub fn expand_deriving_rand(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "rand", "Rand"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -47,7 +46,7 @@ pub fn expand_deriving_rand(cx: &mut ExtCtxt,
|
||||
}
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
|
||||
|
@ -24,14 +24,13 @@ pub fn expand_deriving_show(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
-> ~[@Item] {
|
||||
// &mut ::std::fmt::Formatter
|
||||
let fmtr = Ptr(~Literal(Path::new(~["std", "fmt", "Formatter"])),
|
||||
Borrowed(None, ast::MutMutable));
|
||||
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "fmt", "Show"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -48,7 +47,7 @@ pub fn expand_deriving_show(cx: &mut ExtCtxt,
|
||||
}
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
// we construct a format string and then defer to std::fmt, since that
|
||||
|
@ -21,10 +21,9 @@ pub fn expand_deriving_to_str(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "to_str", "ToStr"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -41,7 +40,7 @@ pub fn expand_deriving_to_str(cx: &mut ExtCtxt,
|
||||
}
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
// It used to be the case that this deriving implementation invoked
|
||||
|
@ -18,10 +18,9 @@ pub fn expand_deriving_zero(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: @MetaItem,
|
||||
in_items: ~[@Item])
|
||||
-> ~[@Item] {
|
||||
-> ~[@Item] {
|
||||
let trait_def = TraitDef {
|
||||
cx: cx, span: span,
|
||||
|
||||
span: span,
|
||||
path: Path::new(~["std", "num", "Zero"]),
|
||||
additional_bounds: ~[],
|
||||
generics: LifetimeBounds::empty(),
|
||||
@ -54,7 +53,7 @@ pub fn expand_deriving_zero(cx: &mut ExtCtxt,
|
||||
}
|
||||
]
|
||||
};
|
||||
trait_def.expand(mitem, in_items)
|
||||
trait_def.expand(cx, mitem, in_items)
|
||||
}
|
||||
|
||||
fn zero_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> @Expr {
|
||||
|
@ -27,5 +27,10 @@ fn foo3(t0: &mut &mut int) {
|
||||
**t1 = 22;
|
||||
}
|
||||
|
||||
fn foo4(t0: & &mut int) {
|
||||
let x: &mut int = &mut **t0; //~ ERROR cannot borrow
|
||||
*x += 1;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user