@@ -33,20 +33,26 @@ use util::ppaux::ty_to_str;
33
33
34
34
use core:: hashmap:: HashSet ;
35
35
use core:: uint;
36
+ use core:: util:: with;
36
37
use syntax:: ast:: m_mutbl;
37
38
use syntax:: ast;
38
39
use syntax:: ast_util;
39
40
use syntax:: codemap:: span;
40
41
use syntax:: print:: pprust;
41
42
use syntax:: visit;
42
43
44
+ struct PurityState {
45
+ def : ast:: node_id ,
46
+ purity : ast:: purity
47
+ }
48
+
43
49
struct CheckLoanCtxt {
44
50
bccx : @BorrowckCtxt ,
45
51
req_maps : ReqMaps ,
46
52
47
53
reported : HashSet < ast:: node_id > ,
48
54
49
- declared_purity : @mut ast :: purity ,
55
+ declared_purity : @mut PurityState ,
50
56
fn_args : @mut @~[ ast:: node_id ]
51
57
}
52
58
@@ -62,14 +68,25 @@ enum purity_cause {
62
68
pc_cmt( bckerr )
63
69
}
64
70
71
+ // if we're not pure, why?
72
+ #[ deriving( Eq ) ]
73
+ enum impurity_cause {
74
+ // some surrounding block was marked as 'unsafe'
75
+ pc_unsafe,
76
+
77
+ // nothing was unsafe, and nothing was pure
78
+ pc_default,
79
+ }
80
+
65
81
pub fn check_loans ( bccx : @BorrowckCtxt ,
66
82
+req_maps : ReqMaps ,
67
83
crate : @ast:: crate ) {
68
84
let clcx = @mut CheckLoanCtxt {
69
85
bccx : bccx,
70
86
req_maps : req_maps,
71
87
reported : HashSet :: new ( ) ,
72
- declared_purity : @mut ast:: impure_fn,
88
+ declared_purity : @mut PurityState { purity : ast:: impure_fn,
89
+ def : 0 } ,
73
90
fn_args : @mut @~[ ]
74
91
} ;
75
92
let vt = visit:: mk_vt ( @visit:: Visitor { visit_expr : check_loans_in_expr,
@@ -106,16 +123,18 @@ pub impl assignment_type {
106
123
pub impl CheckLoanCtxt {
107
124
fn tcx(&self) -> ty::ctxt { self.bccx.tcx }
108
125
109
- fn purity(&mut self, scope_id: ast::node_id) -> Option<purity_cause> {
110
- let default_purity = match *self.declared_purity {
126
+ fn purity(&mut self, scope_id: ast::node_id)
127
+ -> Either<purity_cause, impurity_cause>
128
+ {
129
+ let default_purity = match self.declared_purity.purity {
111
130
// an unsafe declaration overrides all
112
- ast::unsafe_fn => return None ,
131
+ ast::unsafe_fn => return Right(pc_unsafe) ,
113
132
114
133
// otherwise, remember what was declared as the
115
134
// default, but we must scan for requirements
116
135
// imposed by the borrow check
117
- ast::pure_fn => Some (pc_pure_fn),
118
- ast::extern_fn | ast::impure_fn => None
136
+ ast::pure_fn => Left (pc_pure_fn),
137
+ ast::extern_fn | ast::impure_fn => Right(pc_default)
119
138
};
120
139
121
140
// scan to see if this scope or any enclosing scope requires
@@ -125,7 +144,7 @@ pub impl CheckLoanCtxt {
125
144
loop {
126
145
match self.req_maps.pure_map.find(&scope_id) {
127
146
None => (),
128
- Some(e) => return Some (pc_cmt(*e))
147
+ Some(e) => return Left (pc_cmt(*e))
129
148
}
130
149
131
150
match self.tcx().region_maps.opt_encl_scope(scope_id) {
@@ -171,7 +190,7 @@ pub impl CheckLoanCtxt {
171
190
// overloaded operators the callee has an id but no expr.
172
191
// annoying.
173
192
fn check_pure_callee_or_arg(&mut self,
174
- pc: purity_cause,
193
+ pc: Either< purity_cause, impurity_cause> ,
175
194
opt_expr: Option<@ast::expr>,
176
195
callee_id: ast::node_id,
177
196
callee_span: span) {
@@ -196,7 +215,7 @@ pub impl CheckLoanCtxt {
196
215
match opt_expr {
197
216
Some ( expr) => {
198
217
match expr. node {
199
- ast:: expr_path( _) if pc == pc_pure_fn => {
218
+ ast:: expr_path( _) if pc == Left ( pc_pure_fn) => {
200
219
let def = * self . tcx ( ) . def_map . get ( & expr. id ) ;
201
220
let did = ast_util:: def_id_of_def ( def) ;
202
221
let is_fn_arg =
@@ -361,10 +380,10 @@ pub impl CheckLoanCtxt {
361
380
// if this is a pure function, only loan-able state can be
362
381
// assigned, because it is uniquely tied to this function and
363
382
// is not visible from the outside
364
- match self . purity ( ex. id ) {
365
- None => ( ) ,
366
- Some ( pc_cmt ( _ ) ) => {
367
- let purity = self . purity ( ex . id ) . get ( ) ;
383
+ let purity = self . purity ( ex. id ) ;
384
+ match purity {
385
+ Right ( _ ) => ( ) ,
386
+ Left ( pc_cmt ( _ ) ) => {
368
387
// Subtle: Issue #3162. If we are enforcing purity
369
388
// because there is a reference to aliasable, mutable data
370
389
// that we require to be immutable, we can't allow writes
@@ -376,10 +395,10 @@ pub impl CheckLoanCtxt {
376
395
ex. span ,
377
396
at. ing_form ( self . bccx . cmt_to_str ( cmt) ) ) ;
378
397
}
379
- Some ( pc_pure_fn) => {
398
+ Left ( pc_pure_fn) => {
380
399
if cmt. lp . is_none ( ) {
381
400
self . report_purity_error (
382
- pc_pure_fn , ex. span ,
401
+ purity , ex. span ,
383
402
at. ing_form ( self . bccx . cmt_to_str ( cmt) ) ) ;
384
403
}
385
404
}
@@ -462,14 +481,23 @@ pub impl CheckLoanCtxt {
462
481
}
463
482
}
464
483
465
- fn report_purity_error ( & mut self , pc : purity_cause , sp : span , msg : ~str ) {
484
+ fn report_purity_error ( & mut self , pc : Either < purity_cause , impurity_cause > ,
485
+ sp : span , msg : ~str ) {
466
486
match pc {
467
- pc_pure_fn => {
487
+ Right ( pc_default) => { fail ! ( ~"pc_default should be filtered sooner") }
488
+ Right(pc_unsafe) => {
489
+ // this error was prevented by being marked as unsafe, so flag the
490
+ // definition as having contributed to the validity of the program
491
+ let def = self.declared_purity.def;
492
+ debug!(" flagging %? as a used unsafe source", def);
493
+ self.tcx().used_unsafe.insert(def);
494
+ }
495
+ Left(pc_pure_fn) => {
468
496
self.tcx().sess.span_err(
469
497
sp,
470
498
fmt!(" %s prohibited in pure context", msg));
471
499
}
472
- pc_cmt( ref e) => {
500
+ Left( pc_cmt(ref e) ) => {
473
501
if self.reported.insert((*e).cmt.id) {
474
502
self.tcx().sess.span_err(
475
503
(*e).cmt.span,
@@ -556,16 +584,32 @@ pub impl CheckLoanCtxt {
556
584
callee_id: ast::node_id,
557
585
callee_span: span,
558
586
args: &[@ast::expr]) {
559
- match self . purity ( expr. id ) {
560
- None => { }
561
- Some ( ref pc) => {
562
- self . check_pure_callee_or_arg (
563
- ( * pc) , callee, callee_id, callee_span) ;
564
- for args. each |arg| {
565
- self . check_pure_callee_or_arg (
566
- ( * pc) , Some ( * arg) , arg. id , arg. span ) ;
587
+ let pc = self.purity(expr.id);
588
+ match pc {
589
+ // no purity, no need to check for anything
590
+ Right(pc_default) => return,
591
+
592
+ // some form of purity, definitely need to check
593
+ Left(_) => (),
594
+
595
+ // Unsafe trumped. To see if the unsafe is necessary, see what the
596
+ // purity would have been without a trump, and if it's some form
597
+ // of purity then we need to go ahead with the check
598
+ Right(pc_unsafe) => {
599
+ match do with(&mut self.declared_purity.purity,
600
+ ast::impure_fn) { self.purity(expr.id) } {
601
+ Right(pc_unsafe) => fail!(~" unsafe can' t trump twice"),
602
+ Right(pc_default) => return,
603
+ Left(_) => ()
604
+ }
567
605
}
568
- }
606
+
607
+ }
608
+ self.check_pure_callee_or_arg(
609
+ pc, callee, callee_id, callee_span);
610
+ for args.each |arg| {
611
+ self.check_pure_callee_or_arg(
612
+ pc, Some(*arg), arg.id, arg.span);
569
613
}
570
614
}
571
615
}
@@ -580,27 +624,32 @@ fn check_loans_in_fn(fk: &visit::fn_kind,
580
624
let is_stack_closure = self.is_stack_closure(id);
581
625
let fty = ty::node_id_to_type(self.tcx(), id);
582
626
583
- let declared_purity;
627
+ let declared_purity, src ;
584
628
match *fk {
585
629
visit::fk_item_fn(*) | visit::fk_method(*) |
586
630
visit::fk_dtor(*) => {
587
631
declared_purity = ty::ty_fn_purity(fty);
632
+ src = id;
588
633
}
589
634
590
635
visit::fk_anon(*) | visit::fk_fn_block(*) => {
591
636
let fty_sigil = ty::ty_closure_sigil(fty);
592
637
check_moves_from_captured_variables(self, id, fty_sigil);
593
- declared_purity = ty:: determine_inherited_purity (
594
- * self . declared_purity ,
595
- ty:: ty_fn_purity ( fty) ,
638
+ let pair = ty::determine_inherited_purity(
639
+ ( self.declared_purity.purity, self.declared_purity.def) ,
640
+ ( ty::ty_fn_purity(fty), id ),
596
641
fty_sigil);
642
+ declared_purity = pair.first();
643
+ src = pair.second();
597
644
}
598
645
}
599
646
600
647
debug!(" purity on entry=%?", copy self.declared_purity);
601
648
do save_and_restore_managed(self.declared_purity) {
602
649
do save_and_restore_managed(self.fn_args) {
603
- * self . declared_purity = declared_purity;
650
+ self.declared_purity = @mut PurityState {
651
+ purity: declared_purity, def: src
652
+ };
604
653
605
654
match *fk {
606
655
visit::fk_anon(*) |
@@ -754,7 +803,10 @@ fn check_loans_in_block(blk: &ast::blk,
754
803
ast : : default_blk => {
755
804
}
756
805
ast:: unsafe_blk => {
757
- * self . declared_purity = ast:: unsafe_fn;
806
+ * self . declared_purity = PurityState {
807
+ purity : ast:: unsafe_fn,
808
+ def : blk. node . id ,
809
+ } ;
758
810
}
759
811
}
760
812
0 commit comments