@@ -211,7 +211,7 @@ The keywords in [source files](#source-files) are the following strings:
211
211
~~~~~~~~ {.keyword}
212
212
alt again assert
213
213
break
214
- check claim class const copy
214
+ check class const copy
215
215
drop
216
216
else enum export extern
217
217
fail false fn for
@@ -1926,10 +1926,6 @@ an optional reference slot to serve as the function's output, bound to the
1926
1926
` lval ` on the right hand side of the call. If the function eventually returns,
1927
1927
then the expression completes.
1928
1928
1929
- A call expression statically requires that the precondition declared in the
1930
- callee's signature is satisfied by the expression prestate. In this way,
1931
- typestates propagate through function boundaries.
1932
-
1933
1929
An example of a call expression:
1934
1930
1935
1931
~~~~
@@ -2354,117 +2350,6 @@ library. It is best to use the macro forms of logging (*#error*,
2354
2350
when it is changed.
2355
2351
2356
2352
2357
- ### Check expressions
2358
-
2359
- ~~~~~~~~ {.ebnf .gram}
2360
- check_expr : "check" call_expr ;
2361
- ~~~~~~~~
2362
-
2363
- A ` check ` expression connects dynamic assertions made at run-time to the
2364
- static [ typestate system] ( #typestate-system ) . A ` check ` expression takes a
2365
- constraint to check at run-time. If the constraint holds at run-time, control
2366
- passes through the ` check ` and on to the next expression in the enclosing
2367
- block. If the condition fails to hold at run-time, the ` check ` expression
2368
- behaves as a ` fail ` expression.
2369
-
2370
- The typestate algorithm is built around ` check ` expressions, and in particular
2371
- the fact that control * will not pass* a check expression with a condition that
2372
- fails to hold. The typestate algorithm can therefore assume that the (static)
2373
- postcondition of a ` check ` expression includes the checked constraint
2374
- itself. From there, the typestate algorithm can perform dataflow calculations
2375
- on subsequent expressions, propagating [ conditions] ( #conditions ) forward and
2376
- statically comparing implied states and their specifications.
2377
-
2378
- ~~~~~~~~
2379
- # fn print(i: int) { }
2380
-
2381
- pure fn even(x: int) -> bool {
2382
- ret x & 1 == 0;
2383
- }
2384
-
2385
- fn print_even(x: int) : even(x) {
2386
- print(x);
2387
- }
2388
-
2389
- fn test() {
2390
- let y: int = 8;
2391
-
2392
- // Cannot call print_even(y) here.
2393
-
2394
- check even(y);
2395
-
2396
- // Can call print_even(y) here, since even(y) now holds.
2397
- print_even(y);
2398
- }
2399
- ~~~~~~~~
2400
-
2401
- ### Prove expressions
2402
-
2403
- ** Note: Prove expressions are not yet supported by the compiler.**
2404
-
2405
- ~~~~~~~~ {.ebnf .gram}
2406
- prove_expr : "prove" call_expr ;
2407
- ~~~~~~~~
2408
-
2409
- A ` prove ` expression has no run-time effect. Its purpose is to statically
2410
- check (and document) that its argument constraint holds at its expression
2411
- entry point. If its argument typestate does not hold, under the typestate
2412
- algorithm, the program containing it will fail to compile.
2413
-
2414
- ### Claim expressions
2415
-
2416
- ~~~~~~~~ {.ebnf .gram}
2417
- claim_expr : "claim" call_expr ;
2418
- ~~~~~~~~
2419
-
2420
- A ` claim ` expression is an unsafe variant on a ` check ` expression that is not
2421
- actually checked at runtime. Thus, using a ` claim ` implies a proof obligation
2422
- to ensure---without compiler assistance---that an assertion always holds.
2423
-
2424
- Setting a runtime flag can turn all ` claim ` expressions into ` check `
2425
- expressions in a compiled Rust program, but the default is to not check the
2426
- assertion contained in a ` claim ` . The idea behind ` claim ` is that performance
2427
- profiling might identify a few bottlenecks in the code where actually checking
2428
- a given callee's predicate is too expensive; ` claim ` allows the code to
2429
- typecheck without removing the predicate check at every other call site.
2430
-
2431
-
2432
-
2433
- ### If-Check expressions
2434
-
2435
- An ` if check ` expression combines a ` if ` expression and a ` check `
2436
- expression in an indivisible unit that can be used to build more complex
2437
- conditional control-flow than the ` check ` expression affords.
2438
-
2439
- In fact, ` if check ` is a "more primitive" expression than ` check ` ;
2440
- instances of the latter can be rewritten as instances of the former. The
2441
- following two examples are equivalent:
2442
-
2443
- Example using ` check ` :
2444
-
2445
- ~~~~
2446
- # pure fn even(x: int) -> bool { true }
2447
- # fn print_even(x: int) { }
2448
- # let x = 0;
2449
-
2450
- check even(x);
2451
- print_even(x);
2452
- ~~~~
2453
-
2454
- Equivalent example using ` if check ` :
2455
-
2456
- ~~~~
2457
- # pure fn even(x: int) -> bool { true }
2458
- # fn print_even(x: int) { }
2459
- # let x = 0;
2460
-
2461
- if check even(x) {
2462
- print_even(x);
2463
- } else {
2464
- fail;
2465
- }
2466
- ~~~~
2467
-
2468
2353
### Assert expressions
2469
2354
2470
2355
~~~~~~~~ {.ebnf .gram}
@@ -2813,220 +2698,6 @@ Sending operations are not part of the Rust language, but are
2813
2698
implemented in the library. Generic functions that send values bound
2814
2699
the kind of these values to sendable.
2815
2700
2816
-
2817
-
2818
- ## Typestate system
2819
-
2820
-
2821
- Rust programs have a static semantics that determine the types of values
2822
- produced by each expression, as well as the * predicates* that hold over
2823
- slots in the environment at each point in time during execution.
2824
-
2825
- The latter semantics -- the dataflow analysis of predicates holding over slots
2826
- -- is called the * typestate* system.
2827
-
2828
- ### Points
2829
-
2830
- Control flows from statement to statement in a block, and through the
2831
- evaluation of each expression, from one sub-expression to another. This
2832
- sequential control flow is specified as a set of _ points_ , each of which
2833
- has a set of points before and after it in the implied control flow.
2834
-
2835
- For example, this code:
2836
-
2837
- ~~~~~~~~
2838
- # let mut s;
2839
-
2840
- s = ~"hello, world";
2841
- io::println(s);
2842
- ~~~~~~~~
2843
-
2844
- Consists of 2 statements, 3 expressions and 12 points:
2845
-
2846
-
2847
- * the point before the first statement
2848
- * the point before evaluating the static initializer ` "hello, world" `
2849
- * the point after evaluating the static initializer ` "hello, world" `
2850
- * the point after the first statement
2851
- * the point before the second statement
2852
- * the point before evaluating the function value ` println `
2853
- * the point after evaluating the function value ` println `
2854
- * the point before evaluating the arguments to ` println `
2855
- * the point before evaluating the symbol ` s `
2856
- * the point after evaluating the symbol ` s `
2857
- * the point after evaluating the arguments to ` println `
2858
- * the point after the second statement
2859
-
2860
-
2861
- Whereas this code:
2862
-
2863
-
2864
- ~~~~~~~~
2865
- # fn x() -> ~str { ~"" }
2866
- # fn y() -> ~str { ~"" }
2867
-
2868
- io::println(x() + y());
2869
- ~~~~~~~~
2870
-
2871
- Consists of 1 statement, 7 expressions and 14 points:
2872
-
2873
-
2874
- * the point before the statement
2875
- * the point before evaluating the function value ` println `
2876
- * the point after evaluating the function value ` println `
2877
- * the point before evaluating the arguments to ` println `
2878
- * the point before evaluating the arguments to ` + `
2879
- * the point before evaluating the function value ` x `
2880
- * the point after evaluating the function value ` x `
2881
- * the point before evaluating the arguments to ` x `
2882
- * the point after evaluating the arguments to ` x `
2883
- * the point before evaluating the function value ` y `
2884
- * the point after evaluating the function value ` y `
2885
- * the point before evaluating the arguments to ` y `
2886
- * the point after evaluating the arguments to ` y `
2887
- * the point after evaluating the arguments to ` + `
2888
- * the point after evaluating the arguments to ` println `
2889
-
2890
-
2891
- The typestate system reasons over points, rather than statements or
2892
- expressions. This may seem counter-intuitive, but points are the more
2893
- primitive concept. Another way of thinking about a point is as a set of
2894
- * instants in time* at which the state of a task is fixed. By contrast, a
2895
- statement or expression represents a * duration in time* , during which the
2896
- state of the task changes. The typestate system is concerned with constraining
2897
- the possible states of a task's memory at * instants* ; it is meaningless to
2898
- speak of the state of a task's memory "at" a statement or expression, as each
2899
- statement or expression is likely to change the contents of memory.
2900
-
2901
-
2902
- ### Control flow graph
2903
-
2904
- Each * point* can be considered a vertex in a directed * graph* . Each
2905
- kind of expression or statement implies a number of points * and edges* in
2906
- this graph. The edges connect the points within each statement or expression,
2907
- as well as between those points and those of nearby statements and expressions
2908
- in the program. The edges between points represent * possible* indivisible
2909
- control transfers that might occur during execution.
2910
-
2911
- This implicit graph is called the _ control-flow graph_ , or _ CFG_ .
2912
-
2913
-
2914
- ### Constraints
2915
-
2916
- A [ _ predicate_ ] ( #predicate-functions ) is a pure boolean function declared with
2917
- the keywords ` pure fn ` .
2918
-
2919
- A _ constraint_ is a predicate applied to specific slots.
2920
-
2921
- For example, consider the following code:
2922
-
2923
- ~~~~~~~~
2924
- pure fn is_less_than(a: int, b: int) -> bool {
2925
- ret a < b;
2926
- }
2927
-
2928
- fn test() {
2929
- let x: int = 10;
2930
- let y: int = 20;
2931
- check is_less_than(x,y);
2932
- }
2933
- ~~~~~~~~
2934
-
2935
- This example defines the predicate ` is_less_than ` , and applies it to the slots
2936
- ` x ` and ` y ` . The constraint being checked on the third line of the function is
2937
- ` is_less_than(x,y) ` .
2938
-
2939
- Predicates can only apply to slots holding immutable values. The slots a
2940
- predicate applies to can themselves be mutable, but the types of values held
2941
- in those slots must be immutable.
2942
-
2943
- ### Conditions
2944
-
2945
- A _ condition_ is a set of zero or more constraints.
2946
-
2947
- Each * point* has an associated * condition* :
2948
-
2949
- * The _ precondition_ of a statement or expression is the condition required at
2950
- in the point before it.
2951
- * The _ postcondition_ of a statement or expression is the condition enforced
2952
- in the point after it.
2953
-
2954
- Any constraint present in the precondition and * absent* in the postcondition
2955
- is considered to be * dropped* by the statement or expression.
2956
-
2957
-
2958
- ### Calculated typestates
2959
-
2960
- The typestate checking system * calculates* an additional condition for each
2961
- point called its _ typestate_ . For a given statement or expression, we call the
2962
- two typestates associated with its two points the prestate and a poststate.
2963
-
2964
- * The _ prestate_ of a statement or expression is the typestate of the
2965
- point before it.
2966
- * The _ poststate_ of a statement or expression is the typestate of the
2967
- point after it.
2968
-
2969
- A _ typestate_ is a condition that has _ been determined by the typestate
2970
- algorithm_ to hold at a point. This is a subtle but important point to
2971
- understand: preconditions and postconditions are * inputs* to the typestate
2972
- algorithm; prestates and poststates are * outputs* from the typestate
2973
- algorithm.
2974
-
2975
- The typestate algorithm analyses the preconditions and postconditions of every
2976
- statement and expression in a block, and computes a condition for each
2977
- typestate. Specifically:
2978
-
2979
-
2980
- * Initially, every typestate is empty.
2981
- * Each statement or expression's poststate is given the union of the its
2982
- prestate, precondition, and postcondition.
2983
- * Each statement or expression's poststate has the difference between its
2984
- precondition and postcondition removed.
2985
- * Each statement or expression's prestate is given the intersection of the
2986
- poststates of every predecessor point in the CFG.
2987
- * The previous three steps are repeated until no typestates in the
2988
- block change.
2989
-
2990
- The typestate algorithm is a very conventional dataflow calculation, and can
2991
- be performed using bit-set operations, with one bit per predicate and one
2992
- bit-set per condition.
2993
-
2994
- After the typestates of a block are computed, the typestate algorithm checks
2995
- that every constraint in the precondition of a statement is satisfied by its
2996
- prestate. If any preconditions are not satisfied, the mismatch is considered a
2997
- static (compile-time) error.
2998
-
2999
-
3000
- ### Typestate checks
3001
-
3002
- The key mechanism that connects run-time semantics and compile-time analysis
3003
- of typestates is the use of [ ` check ` expressions] ( #check-expressions ) . A
3004
- ` check ` expression guarantees that * if* control were to proceed past it, the
3005
- predicate associated with the ` check ` would have succeeded, so the constraint
3006
- being checked * statically* holds in subsequent points.^[ A ` check ` expression
3007
- is similar to an ` assert ` call in a C program, with the significant difference
3008
- that the Rust compiler * tracks* the constraint that each ` check ` expression
3009
- enforces. Naturally, ` check ` expressions cannot be omitted from a "production
3010
- build" of a Rust program the same way ` asserts ` are frequently disabled in
3011
- deployed C programs.}
3012
-
3013
- It is important to understand that the typestate system has * no insight* into
3014
- the meaning of a particular predicate. Predicates and constraints are not
3015
- evaluated in any way at compile time. Predicates are treated as specific (but
3016
- unknown) functions applied to specific (also unknown) slots. All the typestate
3017
- system does is track which of those predicates -- whatever they calculate --
3018
- * must have been checked already* in order for program control to reach a
3019
- particular point in the CFG. The fundamental building block, therefore, is the
3020
- ` check ` statement, which tells the typestate system "if control passes this
3021
- point, the checked predicate holds".
3022
-
3023
- From this building block, constraints can be propagated to function signatures
3024
- and constrained types, and the responsibility to ` check ` a constraint
3025
- pushed further and further away from the site at which the program requires it
3026
- to hold in order to execute properly.
3027
-
3028
-
3029
-
3030
2701
# Memory and concurrency models
3031
2702
3032
2703
Rust has a memory model centered around concurrently-executing _ tasks_ . Thus
0 commit comments