@@ -149,6 +149,13 @@ fn check_sup_mutbl(req_m: ast::mutability,
149
149
}
150
150
}
151
151
152
+ fn save_and_restore < T : copy , U > ( & t: T , f : fn ( ) -> U ) -> U {
153
+ let old_t = t;
154
+ let u <- f ( ) ;
155
+ t = old_t ;
156
+ ret u;
157
+ }
158
+
152
159
// ----------------------------------------------------------------------
153
160
// Gathering loans
154
161
//
@@ -398,15 +405,18 @@ enum check_loan_ctxt = @{
398
405
// Keep track of whether we're inside a ctor, so as to
399
406
// allow mutating immutable fields in the same class if
400
407
// we are in a ctor, we track the self id
401
- mut in_ctor: bool
408
+ mut in_ctor: bool ,
409
+
410
+ mut is_pure: bool
402
411
} ;
403
412
404
413
fn check_loans ( bccx : borrowck_ctxt ,
405
414
req_loan_map : req_loan_map ,
406
415
crate : @ast:: crate ) {
407
416
let clcx = check_loan_ctxt ( @{ bccx: bccx,
408
417
req_loan_map: req_loan_map,
409
- mut in_ctor : false } ) ;
418
+ mut in_ctor: false ,
419
+ mut is_pure: false } ) ;
410
420
let vt = visit:: mk_vt ( @{ visit_expr: check_loans_in_expr,
411
421
visit_block: check_loans_in_block,
412
422
visit_fn: check_loans_in_fn
@@ -465,6 +475,76 @@ impl methods for check_loan_ctxt {
465
475
}
466
476
}
467
477
478
+ // when we are in a pure context, we check each call to ensure
479
+ // that the function which is invoked is itself pure.
480
+ fn check_pure ( expr : @ast:: expr ) {
481
+ let tcx = self . tcx ( ) ;
482
+ alt ty:: get ( tcx. ty ( expr) ) . struct {
483
+ ty:: ty_fn ( _) {
484
+ // Extract purity or unsafety based on what kind of callee
485
+ // we've got. This would be cleaner if we just admitted
486
+ // that we have an effect system and carried the purity
487
+ // etc around in the type.
488
+
489
+ // First, check the def_map---if expr.id is present then
490
+ // expr must be a path (at least I think that's the idea---NDM)
491
+ let callee_purity = alt tcx. def_map . find ( expr. id ) {
492
+ some ( ast:: def_fn ( _, p) ) { p }
493
+ some ( ast:: def_variant ( _, _) ) { ast:: pure_fn }
494
+ _ {
495
+ // otherwise it may be a method call that we can trace
496
+ // to the def'n site:
497
+ alt self. bccx . method_map . find ( expr. id ) {
498
+ some ( typeck:: method_static ( did) ) {
499
+ if did. crate == ast:: local_crate {
500
+ alt tcx. items . get ( did. node ) {
501
+ ast_map:: node_method ( m, _, _) { m. decl . purity }
502
+ _ { tcx. sess . span_bug ( expr. span ,
503
+ "Node not bound \
504
+ to a method") }
505
+ }
506
+ } else {
507
+ metadata : : csearch:: lookup_method_purity (
508
+ tcx. sess . cstore ,
509
+ did)
510
+ }
511
+ }
512
+ some ( typeck:: method_param ( iid, n_m, _, _) ) |
513
+ some ( typeck:: method_iface ( iid, n_m) ) {
514
+ ty:: iface_methods ( tcx, iid) [ n_m] . purity
515
+ }
516
+ none {
517
+ // otherwise it's just some dang thing. We know
518
+ // it cannot be unsafe because we do not allow
519
+ // unsafe functions to be used as values (or,
520
+ // rather, we only allow that inside an unsafe
521
+ // block, and then it's up to the user to keep
522
+ // things confined).
523
+ ast : : impure_fn
524
+ }
525
+ }
526
+ }
527
+ } ;
528
+
529
+ alt callee_purity {
530
+ ast : : crust_fn | ast:: pure_fn { /*ok*/ }
531
+ ast:: impure_fn {
532
+ self. bccx . span_err (
533
+ expr. span ,
534
+ "pure function calls function \
535
+ not known to be pure") ;
536
+ }
537
+ ast:: unsafe_fn {
538
+ self. bccx . span_err (
539
+ expr. span ,
540
+ "pure function calls unsafe function" ) ;
541
+ }
542
+ }
543
+ }
544
+ _ { /* not a fn, ok */ }
545
+ }
546
+ }
547
+
468
548
fn check_for_conflicting_loans ( scope_id : ast:: node_id ) {
469
549
let new_loanss = alt self . req_loan_map . find ( scope_id) {
470
550
none { ret; }
@@ -532,6 +612,16 @@ impl methods for check_loan_ctxt {
532
612
}
533
613
}
534
614
615
+ // if this is a pure function, only loan-able state can be
616
+ // assigned, because it is uniquely tied to this function and
617
+ // is not visible from the outside
618
+ if self . is_pure && cmt. lp . is_none ( ) {
619
+ self . bccx . span_err (
620
+ ex. span ,
621
+ #fmt[ "%s prohibited in pure functions" ,
622
+ at. ing_form ( self . bccx . cmt_to_str ( cmt) ) ] ) ;
623
+ }
624
+
535
625
// check for a conflicting loan as well, except in the case of
536
626
// taking a mutable ref. that will create a loan of its own
537
627
// which will be checked for compat separately in
@@ -617,21 +707,27 @@ fn check_loans_in_fn(fk: visit::fn_kind, decl: ast::fn_decl, body: ast::blk,
617
707
sp : span , id : ast:: node_id , & & self : check_loan_ctxt ,
618
708
visitor : visit:: vt < check_loan_ctxt > ) {
619
709
620
- let old_in_ctor = self . in_ctor;
621
-
622
- // In principle, we could consider fk_anon(*) or fk_fn_block(*) to
623
- // be in a ctor, I suppose, but the purpose of the in_ctor flag is
624
- // to allow modifications of otherwise immutable fields and
625
- // typestate wouldn't be able to "see" into those functions
626
- // anyway, so it wouldn't be very helpful.
627
- alt fk {
628
- visit:: fk_ctor( * ) { self . in_ctor = true; }
629
- _ { self . in_ctor = false; }
630
- } ;
710
+ save_and_restore ( self . in_ctor ) { ||
711
+ save_and_restore ( self . is_pure ) { ||
712
+ // In principle, we could consider fk_anon(*) or
713
+ // fk_fn_block(*) to be in a ctor, I suppose, but the
714
+ // purpose of the in_ctor flag is to allow modifications
715
+ // of otherwise immutable fields and typestate wouldn't be
716
+ // able to "see" into those functions anyway, so it
717
+ // wouldn't be very helpful.
718
+ alt fk {
719
+ visit : : fk_ctor ( * ) { self . in_ctor = true ; }
720
+ _ { self. in_ctor = false ; }
721
+ } ;
631
722
632
- visit:: visit_fn( fk, decl, body, sp, id, self , visitor) ;
723
+ alt decl. purity {
724
+ ast:: pure_fn { self. is_pure = true ; }
725
+ _ { }
726
+ }
633
727
634
- self . in_ctor = old_in_ctor;
728
+ visit:: visit_fn ( fk, decl, body, sp, id, self , visitor) ;
729
+ }
730
+ }
635
731
}
636
732
637
733
fn check_loans_in_expr ( expr : @ast:: expr ,
@@ -680,6 +776,10 @@ fn check_loans_in_expr(expr: @ast::expr,
680
776
}
681
777
}
682
778
ast:: expr_call ( f, args, _) {
779
+ if self . is_pure {
780
+ self . check_pure ( f) ;
781
+ for args. each { |arg| self . check_pure( arg) }
782
+ }
683
783
let arg_tys = ty:: ty_fn_args ( ty:: expr_ty ( self . tcx ( ) , f) ) ;
684
784
vec:: iter2 ( args, arg_tys) { |arg, arg_ty|
685
785
alt ty:: resolved_mode ( self . tcx ( ) , arg_ty. mode ) {
@@ -696,14 +796,25 @@ fn check_loans_in_expr(expr: @ast::expr,
696
796
}
697
797
_ { }
698
798
}
799
+
699
800
visit:: visit_expr ( expr, self , vt) ;
700
801
}
701
802
702
803
fn check_loans_in_block ( blk : ast:: blk ,
703
804
& & self : check_loan_ctxt ,
704
805
vt : visit:: vt < check_loan_ctxt > ) {
705
- self . check_for_conflicting_loans( blk. node. id) ;
706
- visit:: visit_block( blk, self , vt) ;
806
+ save_and_restore ( self . is_pure ) { ||
807
+ self . check_for_conflicting_loans ( blk. node . id ) ;
808
+
809
+ alt blk. node . rules {
810
+ ast:: default_blk {
811
+ }
812
+ ast:: unchecked_blk |
813
+ ast:: unsafe_blk { self. is_pure = false ; }
814
+ }
815
+
816
+ visit:: visit_block ( blk, self , vt) ;
817
+ }
707
818
}
708
819
709
820
// ----------------------------------------------------------------------
@@ -1022,20 +1133,25 @@ impl categorize_methods for borrowck_ctxt {
1022
1133
// lp: loan path, must be none for aliasable things
1023
1134
let { m, lp} = alt ty:: resolved_mode ( self . tcx , mode) {
1024
1135
ast:: by_mutbl_ref {
1025
- { m : m_mutbl, lp : none}
1136
+ { m : m_mutbl,
1137
+ lp : none}
1026
1138
}
1027
1139
ast:: by_move | ast:: by_copy {
1028
- { m : m_mutbl, lp : some( @lp_arg( vid) ) }
1140
+ { m : m_mutbl,
1141
+ lp : some ( @lp_arg ( vid) ) }
1029
1142
}
1030
1143
ast:: by_ref {
1031
- if TREAT_CONST_AS_IMM {
1032
- { m : m_imm, lp : some( @lp_arg( vid) ) }
1033
- } else {
1034
- { m: m_const, lp: none}
1035
- }
1144
+ { m : if TREAT_CONST_AS_IMM { m_imm} else { m_const} ,
1145
+ lp : none}
1036
1146
}
1037
1147
ast:: by_val {
1038
- { m : m_imm, lp : some( @lp_arg( vid) ) }
1148
+ // by-value is this hybrid mode where we have a
1149
+ // pointer but we do not own it. This is not
1150
+ // considered loanable because, for example, a by-ref
1151
+ // and and by-val argument might both actually contain
1152
+ // the same unique ptr.
1153
+ { m : m_imm,
1154
+ lp : none}
1039
1155
}
1040
1156
} ;
1041
1157
@{ id: id, span: span,
0 commit comments