Skip to content

Commit dc69018

Browse files
committed
librustc: Make the compiler ignore purity.
For bootstrapping purposes, this commit does not remove all uses of the keyword "pure" -- doing so would cause the compiler to no longer bootstrap due to some syntax extensions ("deriving" in particular). Instead, it makes the compiler ignore "pure". Post-snapshot, we can remove "pure" from the language. There are quite a few (~100) borrow check errors that were essentially all the result of mutable fields or partial borrows of `@mut`. Per discussions with Niko I think we want to allow partial borrows of `@mut` but detect obvious footguns. We should also improve the error message when `@mut` is erroneously reborrowed.
1 parent fc8c808 commit dc69018

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+373
-540
lines changed

doc/rust.md

+4-41
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ false fn for
214214
if impl
215215
let loop
216216
match mod mut
217-
priv pub pure
217+
priv pub
218218
ref return
219219
self static struct super
220220
true trait type
@@ -936,7 +936,6 @@ Specifically, the following operations are considered unsafe:
936936

937937
- Dereferencing a [raw pointer](#pointer-types).
938938
- Casting a [raw pointer](#pointer-types) to a safe pointer type.
939-
- Breaking the [purity-checking rules](#pure-functions) in a `pure` function.
940939
- Calling an unsafe function.
941940

942941
##### Unsafe blocks
@@ -946,42 +945,6 @@ This facility exists because the static semantics of Rust are a necessary approx
946945
When a programmer has sufficient conviction that a sequence of unsafe operations is actually safe, they can encapsulate that sequence (taken as a whole) within an `unsafe` block. The compiler will consider uses of such code "safe", to the surrounding context.
947946

948947

949-
#### Pure functions
950-
951-
A pure function declaration is identical to a function declaration, except that
952-
it is declared with the additional keyword `pure`. In addition, the typechecker
953-
checks the body of a pure function with a restricted set of typechecking rules.
954-
A pure function may only modify data owned by its own stack frame.
955-
So, a pure function may modify a local variable allocated on the stack, but not a mutable reference that it takes as an argument.
956-
A pure function may only call other pure functions, not general functions.
957-
958-
An example of a pure function:
959-
960-
~~~~
961-
pure fn lt_42(x: int) -> bool {
962-
return (x < 42);
963-
}
964-
~~~~
965-
966-
Pure functions may call other pure functions:
967-
968-
~~~~{.xfail-test}
969-
pure fn pure_length<T>(ls: List<T>) -> uint { ... }
970-
971-
pure fn nonempty_list<T>(ls: List<T>) -> bool { pure_length(ls) > 0u }
972-
~~~~
973-
974-
These purity-checking rules approximate the concept of referential transparency:
975-
that a call-expression could be rewritten with the literal-expression of its return value, without changing the meaning of the program.
976-
Since they are an approximation, sometimes these rules are *too* restrictive.
977-
Rust allows programmers to violate these rules using [`unsafe` blocks](#unsafe-blocks), which we already saw.
978-
As with any `unsafe` block, those that violate static purity carry transfer the burden of safety-proof from the compiler to the programmer.
979-
Programmers should exercise caution when breaking such rules.
980-
981-
For more details on purity, see [the borrowed pointer tutorial][borrow].
982-
983-
[borrow]: tutorial-borrowed-ptr.html
984-
985948
#### Diverging functions
986949

987950
A special kind of function can be declared with a `!` character where the
@@ -1246,10 +1209,10 @@ For example:
12461209

12471210
~~~~
12481211
trait Num {
1249-
static pure fn from_int(n: int) -> Self;
1212+
static fn from_int(n: int) -> Self;
12501213
}
12511214
impl Num for float {
1252-
static pure fn from_int(n: int) -> float { n as float }
1215+
static fn from_int(n: int) -> float { n as float }
12531216
}
12541217
let x: float = Num::from_int(42);
12551218
~~~~
@@ -2643,7 +2606,7 @@ Raw pointers (`*`)
26432606
### Function types
26442607

26452608
The function type-constructor `fn` forms new function types. A function type
2646-
consists of a set of function-type modifiers (`pure`, `unsafe`, `extern`, etc.),
2609+
consists of a set of function-type modifiers (`unsafe`, `extern`, etc.),
26472610
a sequence of input slots and an output slot.
26482611

26492612
An example of a `fn` type:

doc/tutorial-borrowed-ptr.md

+17-17
Original file line numberDiff line numberDiff line change
@@ -486,12 +486,12 @@ For example, we could write a subroutine like this:
486486

487487
~~~
488488
struct Point {x: float, y: float}
489-
fn get_x(p: &r/Point) -> &r/float { &p.x }
489+
fn get_x(p: &'r Point) -> &'r float { &p.x }
490490
~~~
491491

492492
Here, the function `get_x()` returns a pointer into the structure it
493-
was given. The type of the parameter (`&r/Point`) and return type
494-
(`&r/float`) both use a new syntactic form that we have not seen so
493+
was given. The type of the parameter (`&'r Point`) and return type
494+
(`&'r float`) both use a new syntactic form that we have not seen so
495495
far. Here the identifier `r` names the lifetime of the pointer
496496
explicitly. So in effect, this function declares that it takes a
497497
pointer with lifetime `r` and returns a pointer with that same
@@ -572,8 +572,8 @@ function:
572572
# Rectangle(Point, Size) // upper-left, dimensions
573573
# }
574574
# fn compute_area(shape: &Shape) -> float { 0f }
575-
fn select<T>(shape: &r/Shape, threshold: float,
576-
a: &r/T, b: &r/T) -> &r/T {
575+
fn select<T>(shape: &'r Shape, threshold: float,
576+
a: &'r T, b: &'r T) -> &'r T {
577577
if compute_area(shape) > threshold {a} else {b}
578578
}
579579
~~~
@@ -593,17 +593,17 @@ example:
593593
# }
594594
# fn compute_area(shape: &Shape) -> float { 0f }
595595
# fn select<T>(shape: &Shape, threshold: float,
596-
# a: &r/T, b: &r/T) -> &r/T {
596+
# a: &'r T, b: &'r T) -> &'r T {
597597
# if compute_area(shape) > threshold {a} else {b}
598598
# }
599-
// -+ r
600-
fn select_based_on_unit_circle<T>( // |-+ B
601-
threshold: float, a: &r/T, b: &r/T) -> &r/T { // | |
602-
// | |
603-
let shape = Circle(Point {x: 0., y: 0.}, 1.); // | |
604-
select(&shape, threshold, a, b) // | |
605-
} // |-+
606-
// -+
599+
// -+ r
600+
fn select_based_on_unit_circle<T>( // |-+ B
601+
threshold: float, a: &'r T, b: &'r T) -> &'r T { // | |
602+
// | |
603+
let shape = Circle(Point {x: 0., y: 0.}, 1.); // | |
604+
select(&shape, threshold, a, b) // | |
605+
} // |-+
606+
// -+
607607
~~~
608608

609609
In this call to `select()`, the lifetime of the first parameter shape
@@ -629,8 +629,8 @@ returned. Here is how the new `select()` might look:
629629
# Rectangle(Point, Size) // upper-left, dimensions
630630
# }
631631
# fn compute_area(shape: &Shape) -> float { 0f }
632-
fn select<T>(shape: &tmp/Shape, threshold: float,
633-
a: &r/T, b: &r/T) -> &r/T {
632+
fn select<T>(shape: &'tmp Shape, threshold: float,
633+
a: &'r T, b: &'r T) -> &'r T {
634634
if compute_area(shape) > threshold {a} else {b}
635635
}
636636
~~~
@@ -649,7 +649,7 @@ concise to just omit the named lifetime for `shape` altogether:
649649
# }
650650
# fn compute_area(shape: &Shape) -> float { 0f }
651651
fn select<T>(shape: &Shape, threshold: float,
652-
a: &r/T, b: &r/T) -> &r/T {
652+
a: &'r T, b: &'r T) -> &'r T {
653653
if compute_area(shape) > threshold {a} else {b}
654654
}
655655
~~~

src/libcore/cell.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,29 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use cast::transmute;
1112
use option;
1213
use prelude::*;
1314

1415
/// A dynamic, mutable location.
1516
///
1617
/// Similar to a mutable option type, but friendlier.
1718
18-
#[deriving_eq]
1919
pub struct Cell<T> {
2020
mut value: Option<T>
2121
}
2222

23+
impl<T:cmp::Eq> cmp::Eq for Cell<T> {
24+
pure fn eq(&self, other: &Cell<T>) -> bool {
25+
unsafe {
26+
let frozen_self: &Option<T> = transmute(&mut self.value);
27+
let frozen_other: &Option<T> = transmute(&mut other.value);
28+
frozen_self == frozen_other
29+
}
30+
}
31+
pure fn ne(&self, other: &Cell<T>) -> bool { !self.eq(other) }
32+
}
33+
2334
/// Creates a new full cell with the given value.
2435
pub fn Cell<T>(value: T) -> Cell<T> {
2536
Cell { value: Some(value) }

src/libcore/comm.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use cast;
1112
use either::{Either, Left, Right};
1213
use kinds::Owned;
1314
use option;
1415
use option::{Option, Some, None, unwrap};
16+
use uint;
1517
use unstable;
1618
use vec;
1719

@@ -283,8 +285,12 @@ impl<T: Owned> Peekable<T> for PortSet<T> {
283285
pure fn port_set_peek<T:Owned>(self: &PortSet<T>) -> bool {
284286
// It'd be nice to use self.port.each, but that version isn't
285287
// pure.
286-
for vec::each(self.ports) |p| {
287-
if p.peek() { return true }
288+
for uint::range(0, vec::uniq_len(&const self.ports)) |i| {
289+
// XXX: Botch pending demuting.
290+
unsafe {
291+
let port: &Port<T> = cast::transmute(&mut self.ports[i]);
292+
if port.peek() { return true }
293+
}
288294
}
289295
false
290296
}

src/libcore/container.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ use option::Option;
1414

1515
pub trait Container {
1616
/// Return the number of elements in the container
17-
pure fn len(&self) -> uint;
17+
pure fn len(&const self) -> uint;
1818

1919
/// Return true if the container contains no elements
20-
pure fn is_empty(&self) -> bool;
20+
pure fn is_empty(&const self) -> bool;
2121
}
2222

2323
pub trait Mutable: Container {

src/libcore/hashmap.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,10 @@ pub mod linear {
290290
291291
impl<K:Hash + IterBytes + Eq,V> Container for LinearMap<K, V> {
292292
/// Return the number of elements in the map
293-
pure fn len(&self) -> uint { self.size }
293+
pure fn len(&const self) -> uint { self.size }
294294
295295
/// Return true if the map contains no elements
296-
pure fn is_empty(&self) -> bool { self.len() == 0 }
296+
pure fn is_empty(&const self) -> bool { self.len() == 0 }
297297
}
298298
299299
impl<K:Hash + IterBytes + Eq,V> Mutable for LinearMap<K, V> {
@@ -555,10 +555,10 @@ pub mod linear {
555555

556556
impl<T:Hash + IterBytes + Eq> Container for LinearSet<T> {
557557
/// Return the number of elements in the set
558-
pure fn len(&self) -> uint { self.map.len() }
558+
pure fn len(&const self) -> uint { self.map.len() }
559559

560560
/// Return true if the set contains no elements
561-
pure fn is_empty(&self) -> bool { self.map.is_empty() }
561+
pure fn is_empty(&const self) -> bool { self.map.is_empty() }
562562
}
563563

564564
impl<T:Hash + IterBytes + Eq> Mutable for LinearSet<T> {

src/libcore/io.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ pub struct BytesWriter {
11161116
impl Writer for BytesWriter {
11171117
fn write(&self, v: &[const u8]) {
11181118
let v_len = v.len();
1119-
let bytes_len = self.bytes.len();
1119+
let bytes_len = vec::uniq_len(&const self.bytes);
11201120
11211121
let count = uint::max(bytes_len, self.pos + v_len);
11221122
vec::reserve(&mut self.bytes, count);
@@ -1131,7 +1131,7 @@ impl Writer for BytesWriter {
11311131
}
11321132
fn seek(&self, offset: int, whence: SeekStyle) {
11331133
let pos = self.pos;
1134-
let len = self.bytes.len();
1134+
let len = vec::uniq_len(&const self.bytes);
11351135
self.pos = seek_in_buf(offset, pos, len, whence);
11361136
}
11371137
fn tell(&self) -> uint { self.pos }

src/libcore/mutable.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ pub fn unwrap<T>(m: Mut<T>) -> T {
4646
pub impl<T> Data<T> {
4747
fn borrow_mut<R>(&self, op: &fn(t: &mut T) -> R) -> R {
4848
match self.mode {
49-
Immutable => fail!(fmt!("%? currently immutable",
50-
self.value)),
49+
Immutable => fail!(~"currently immutable"),
5150
ReadOnly | Mutable => {}
5251
}
5352
@@ -62,8 +61,7 @@ pub impl<T> Data<T> {
6261
6362
fn borrow_imm<R>(&self, op: &fn(t: &T) -> R) -> R {
6463
match self.mode {
65-
Mutable => fail!(fmt!("%? currently mutable",
66-
self.value)),
64+
Mutable => fail!(~"currently mutable"),
6765
ReadOnly | Immutable => {}
6866
}
6967

src/libcore/option.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -228,14 +228,14 @@ pub pure fn while_some<T>(x: Option<T>, blk: &fn(v: T) -> Option<T>) {
228228
}
229229
230230
#[inline(always)]
231-
pub pure fn is_none<T>(opt: &Option<T>) -> bool {
231+
pub pure fn is_none<T>(opt: &const Option<T>) -> bool {
232232
//! Returns true if the option equals `none`
233233
234234
match *opt { None => true, Some(_) => false }
235235
}
236236
237237
#[inline(always)]
238-
pub pure fn is_some<T>(opt: &Option<T>) -> bool {
238+
pub pure fn is_some<T>(opt: &const Option<T>) -> bool {
239239
//! Returns true if the option contains some value
240240
241241
!is_none(opt)
@@ -333,11 +333,11 @@ impl<T> MutableIter<T> for Option<T> {
333333
pub impl<T> Option<T> {
334334
/// Returns true if the option equals `none`
335335
#[inline(always)]
336-
pure fn is_none(&self) -> bool { is_none(self) }
336+
pure fn is_none(&const self) -> bool { is_none(self) }
337337
338338
/// Returns true if the option contains some value
339339
#[inline(always)]
340-
pure fn is_some(&self) -> bool { is_some(self) }
340+
pure fn is_some(&const self) -> bool { is_some(self) }
341341
342342
/**
343343
* Update an optional value by optionally running its content by reference

src/libcore/ptr.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -223,20 +223,20 @@ pub unsafe fn array_each<T>(arr: **T, cb: &fn(*T)) {
223223
}
224224
225225
pub trait Ptr<T> {
226-
pure fn is_null(&self) -> bool;
227-
pure fn is_not_null(&self) -> bool;
226+
pure fn is_null(&const self) -> bool;
227+
pure fn is_not_null(&const self) -> bool;
228228
pure fn offset(&self, count: uint) -> Self;
229229
}
230230
231231
/// Extension methods for immutable pointers
232232
impl<T> Ptr<T> for *T {
233233
/// Returns true if the pointer is equal to the null pointer.
234234
#[inline(always)]
235-
pure fn is_null(&self) -> bool { is_null(*self) }
235+
pure fn is_null(&const self) -> bool { is_null(*self) }
236236
237237
/// Returns true if the pointer is not equal to the null pointer.
238238
#[inline(always)]
239-
pure fn is_not_null(&self) -> bool { is_not_null(*self) }
239+
pure fn is_not_null(&const self) -> bool { is_not_null(*self) }
240240
241241
/// Calculates the offset from a pointer.
242242
#[inline(always)]
@@ -247,11 +247,11 @@ impl<T> Ptr<T> for *T {
247247
impl<T> Ptr<T> for *mut T {
248248
/// Returns true if the pointer is equal to the null pointer.
249249
#[inline(always)]
250-
pure fn is_null(&self) -> bool { is_null(*self) }
250+
pure fn is_null(&const self) -> bool { is_null(*self) }
251251
252252
/// Returns true if the pointer is not equal to the null pointer.
253253
#[inline(always)]
254-
pure fn is_not_null(&self) -> bool { is_not_null(*self) }
254+
pure fn is_not_null(&const self) -> bool { is_not_null(*self) }
255255
256256
/// Calculates the offset from a mutable pointer.
257257
#[inline(always)]

src/libcore/repr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ impl TyVisitor for ReprVisitor {
499499
}
500500

501501
fn visit_enum_variant_field(&self, i: uint, inner: *TyDesc) -> bool {
502-
match self.var_stk[self.var_stk.len() - 1] {
502+
match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] {
503503
Degenerate | TagMatch => {
504504
if i != 0 {
505505
self.writer.write_str(", ");
@@ -517,7 +517,7 @@ impl TyVisitor for ReprVisitor {
517517
_disr_val: int,
518518
n_fields: uint,
519519
_name: &str) -> bool {
520-
match self.var_stk[self.var_stk.len() - 1] {
520+
match self.var_stk[vec::uniq_len(&const self.var_stk) - 1] {
521521
Degenerate | TagMatch => {
522522
if n_fields > 0 {
523523
self.writer.write_char(')');

src/libcore/task/spawn.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ type TaskGroupInner = &'self mut Option<TaskGroupData>;
127127

128128
// A taskgroup is 'dead' when nothing can cause it to fail; only members can.
129129
pure fn taskgroup_is_dead(tg: &TaskGroupData) -> bool {
130-
(&tg.members).is_empty()
130+
(&const tg.members).is_empty()
131131
}
132132

133133
// A list-like structure by which taskgroups keep track of all ancestor groups

0 commit comments

Comments
 (0)