@@ -38,7 +38,8 @@ fn check_crate(tcx: ty::ctxt,
38
38
let req_maps = if msg_level > 0 u {
39
39
gather_loans ( bccx, crate )
40
40
} else {
41
- { req_loan_map : int_hash( ) }
41
+ { req_loan_map : int_hash( ) ,
42
+ pure_map: int_hash ( ) }
42
43
} ;
43
44
check_loans ( bccx, req_maps, crate ) ;
44
45
ret ( bccx. root_map , bccx. mutbl_map ) ;
@@ -165,14 +166,16 @@ fn root_map() -> root_map {
165
166
// sure that all of these loans are honored.
166
167
167
168
type req_maps = {
168
- req_loan_map : hashmap < ast:: node_id , @mut [ @const [ loan ] ] >
169
+ req_loan_map : hashmap < ast:: node_id , @mut [ @const [ loan ] ] > ,
170
+ pure_map : hashmap < ast:: node_id , bckerr >
169
171
} ;
170
172
171
173
enum gather_loan_ctxt = @{ bccx: borrowck_ctxt, req_maps: req_maps} ;
172
174
173
175
fn gather_loans ( bccx : borrowck_ctxt , crate : @ast:: crate ) -> req_maps {
174
176
let glcx = gather_loan_ctxt ( @{ bccx: bccx,
175
- req_maps : { req_loan_map : int_hash ( ) } } ) ;
177
+ req_maps : { req_loan_map : int_hash ( ) ,
178
+ pure_map : int_hash ( ) } } ) ;
176
179
let v = visit:: mk_vt ( @{ visit_expr: req_loans_in_expr
177
180
with * visit:: default_visitor ( ) } ) ;
178
181
visit:: visit_crate ( * crate , glcx, v) ;
@@ -293,15 +296,40 @@ impl methods for gather_loan_ctxt {
293
296
// it dynamically (or see that it is preserved by virtue of being
294
297
// rooted in some immutable path)
295
298
none {
296
- self. bccx. report_if_err(
297
- self . check_mutbl( req_mutbl, cmt) . chain { |_ok |
298
- let opt_scope_id = alt scope_r {
299
- ty : : re_scope( scope_id) { some( scope_id) }
300
- _ { none }
301
- } ;
299
+ let opt_scope_id = alt scope_r {
300
+ ty:: re_scope( scope_id) { some( scope_id) }
301
+ _ { none }
302
+ } ;
302
303
304
+ let result = {
305
+ self . check_mutbl( req_mutbl, cmt) . chain { |_ok|
303
306
self . bccx. preserve( cmt, opt_scope_id)
304
- } )
307
+ }
308
+ } ;
309
+
310
+ alt result {
311
+ ok( ( ) ) {
312
+ // we were able guarantee the validity of the ptr,
313
+ // perhaps by rooting or because it is immutably
314
+ // rooted. good.
315
+ }
316
+ err( e) {
317
+ // not able to guarantee the validity of the ptr.
318
+ // rather than report an error, presuming that the
319
+ // borrow is for a limited scope, we'll make one last
320
+ // ditch effort and require that the scope where the
321
+ // borrow occurs be pure.
322
+ alt opt_scope_id {
323
+ some( scope_id) {
324
+ self . req_maps. pure_map. insert( scope_id, e) ;
325
+ }
326
+ none {
327
+ // otherwise, fine, I give up.
328
+ self. bccx. report( e) ;
329
+ }
330
+ }
331
+ }
332
+ }
305
333
}
306
334
}
307
335
}
@@ -475,16 +503,30 @@ enum check_loan_ctxt = @{
475
503
// we are in a ctor, we track the self id
476
504
mut in_ctor: bool ,
477
505
478
- mut is_pure: bool
506
+ mut is_pure: purity_cause
479
507
} ;
480
508
509
+ // if we are enforcing purity, why are we doing so?
510
+ enum purity_cause {
511
+ // not enforcing purity:
512
+ pc_impure,
513
+
514
+ // enforcing purity because fn was declared pure:
515
+ pc_declaration,
516
+
517
+ // enforce purity because we need to guarantee the
518
+ // validity of some alias; `bckerr` describes the
519
+ // reason we needed to enforce purity.
520
+ pc_cmt( bckerr)
521
+ }
522
+
481
523
fn check_loans( bccx: borrowck_ctxt,
482
524
req_maps: req_maps,
483
525
crate : @ast:: crate) {
484
526
let clcx = check_loan_ctxt ( @{ bccx : bccx,
485
527
req_maps : req_maps,
486
528
mut in_ctor : false ,
487
- mut is_pure : false } ) ;
529
+ mut is_pure : pc_impure } ) ;
488
530
let vt = visit:: mk_vt( @{ visit_expr: check_loans_in_expr,
489
531
visit_block: check_loans_in_block,
490
532
visit_fn: check_loans_in_fn
@@ -595,24 +637,37 @@ impl methods for check_loan_ctxt {
595
637
} ;
596
638
597
639
alt callee_purity {
598
- ast : : crust_fn | ast:: pure_fn { /*ok*/ }
599
- ast:: impure_fn {
600
- self. bccx. span_err(
601
- expr. span,
602
- "pure function calls function \
603
- not known to be pure") ;
640
+ ast : : crust_fn | ast:: pure_fn {
641
+ /*ok*/
604
642
}
605
- ast:: unsafe_fn {
643
+ ast:: impure_fn | ast :: unsafe_fn {
606
644
self. bccx. span_err(
607
645
expr. span,
608
- "pure function calls unsafe function" ) ;
646
+ "access to non-pure functions \
647
+ prohibited in a pure context") ;
648
+ self . report_why_pure( ) ;
609
649
}
610
650
}
611
651
}
612
652
_ { /* not a fn, ok */ }
613
653
}
614
654
}
615
655
656
+ fn check_for_purity_requirement( scope_id: ast:: node_id) {
657
+ // if we are not already enforcing purity, check whether the
658
+ // gather pass thought we needed to enforce purity for this
659
+ // scope.
660
+ alt self. is_pure {
661
+ pc_declaration | pc_cmt( * ) { }
662
+ pc_impure {
663
+ alt self. req_maps. pure_map. find( scope_id) {
664
+ none { }
665
+ some( e) { self . is_pure = pc_cmt( e) }
666
+ }
667
+ }
668
+ }
669
+ }
670
+
616
671
fn check_for_conflicting_loans( scope_id: ast:: node_id) {
617
672
let new_loanss = alt self . req_maps. req_loan_map. find( scope_id) {
618
673
none { ret; }
@@ -683,11 +738,12 @@ impl methods for check_loan_ctxt {
683
738
// if this is a pure function, only loan-able state can be
684
739
// assigned, because it is uniquely tied to this function and
685
740
// is not visible from the outside
686
- if self . is_pure && cmt. lp. is_none( ) {
741
+ if self . is_pure != pc_impure && cmt. lp. is_none( ) {
687
742
self . bccx. span_err(
688
743
ex. span,
689
- #fmt[ "%s prohibited in pure functions ",
744
+ #fmt[ "%s prohibited in a pure context " ,
690
745
at. ing_form( self . bccx. cmt_to_str( cmt) ) ] ) ;
746
+ self . report_why_pure( ) ;
691
747
}
692
748
693
749
// check for a conflicting loan as well, except in the case of
@@ -720,6 +776,23 @@ impl methods for check_loan_ctxt {
720
776
self . bccx. add_to_mutbl_map( cmt) ;
721
777
}
722
778
779
+ fn report_why_pure( ) {
780
+ alt self. is_pure {
781
+ pc_impure {
782
+ self. tcx( ) . sess. bug( "report_why_pure() called when impure" ) ;
783
+ }
784
+ pc_declaration {
785
+ // fn was declared pure; no need to report this, I think
786
+ }
787
+ pc_cmt( e) {
788
+ self . tcx( ) . sess. span_note(
789
+ e. cmt. span,
790
+ #fmt[ "pure context is required due to an illegal borrow: %s" ,
791
+ self . bccx. bckerr_code_to_str( e. code) ] ) ;
792
+ }
793
+ }
794
+ }
795
+
723
796
fn check_move_out( ex: @ast:: expr) {
724
797
let cmt = self . bccx. cat_expr( ex) ;
725
798
self . check_move_out_from_cmt( cmt) ;
@@ -788,8 +861,11 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
788
861
_ { self . in_ctor = false; }
789
862
} ;
790
863
864
+ // NDM this doesn't seem algother right, what about fn items
865
+ // nested in pure fns? etc?
866
+
791
867
alt decl. purity {
792
- ast:: pure_fn { self . is_pure = true ; }
868
+ ast:: pure_fn { self . is_pure = pc_declaration ; }
793
869
_ { }
794
870
}
795
871
@@ -802,6 +878,16 @@ fn check_loans_in_expr(expr: @ast::expr,
802
878
& & self : check_loan_ctxt,
803
879
vt: visit:: vt < check_loan_ctxt > ) {
804
880
self . check_for_conflicting_loans( expr. id) ;
881
+ save_and_restore( self . is_pure) { ||
882
+ self . check_for_purity_requirement( expr. id) ;
883
+ check_loans_in_expr_1( expr, self , vt) ;
884
+ }
885
+ }
886
+
887
+ // avoid rightward drift by breaking this out into its own fn
888
+ fn check_loans_in_expr_1( expr: @ast:: expr,
889
+ & & self : check_loan_ctxt,
890
+ vt: visit:: vt < check_loan_ctxt > ) {
805
891
alt expr. node {
806
892
ast : : expr_swap( l, r) {
807
893
self . check_assignment( at_swap, l) ;
@@ -844,7 +930,7 @@ fn check_loans_in_expr(expr: @ast::expr,
844
930
}
845
931
}
846
932
ast:: expr_call( f, args, _) {
847
- if self . is_pure {
933
+ if self . is_pure != pc_impure {
848
934
self. check_pure( f) ;
849
935
for args. each { |arg| self . check_pure( arg) }
850
936
}
@@ -873,12 +959,27 @@ fn check_loans_in_block(blk: ast::blk,
873
959
vt: visit:: vt < check_loan_ctxt > ) {
874
960
save_and_restore( self . is_pure) { ||
875
961
self . check_for_conflicting_loans( blk. node. id) ;
962
+ self . check_for_purity_requirement( blk. node. id) ;
876
963
877
964
alt blk. node. rules {
878
965
ast : : default_blk {
879
966
}
880
- ast:: unchecked_blk |
881
- ast:: unsafe_blk { self. is_pure = false; }
967
+ ast:: unchecked_blk {
968
+ alt self. is_pure {
969
+ pc_impure | pc_declaration {
970
+ self. is_pure = pc_impure;
971
+ }
972
+ pc_cmt( _) {
973
+ // unchecked does not override purity requirements due
974
+ // to borrows; unchecked didn't seem strong enough to
975
+ // justify potential memory unsafety to me
976
+ }
977
+ }
978
+ }
979
+ ast:: unsafe_blk {
980
+ // unsafe blocks override everything
981
+ self. is_pure = pc_impure;
982
+ }
882
983
}
883
984
884
985
visit:: visit_block( blk, self , vt) ;
0 commit comments