Skip to content

Commit 8896852

Browse files
committed
Fixed some typos in draft.html
1 parent be5fe7c commit 8896852

File tree

2 files changed

+20
-20
lines changed

2 files changed

+20
-20
lines changed

docs/draft.html

+12-12
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ <h3 data-number="1.5.2" id="type-safety"><span class="header-section-number">1.5
926926
or a <em>linear</em> type system. Unless explicitly initialized, objects
927927
start out <em>uninitialized</em>. They can’t be used in this state. When
928928
you assign to an object, it becomes initialized. When you relocate from
929-
an object, it’s value is moved and it’s reset to uninitialized. If you
929+
an object, its value is moved and it’s reset to uninitialized. If you
930930
relocate from an object inside control flow, it becomes <em>potentially
931931
uninitialized</em>, and its destructor is conditionally executed after
932932
reading a compiler-generated drop flag.</p>
@@ -1341,9 +1341,9 @@ <h3 data-number="1.5.4" id="runtime-checks"><span class="header-section-number">
13411341
establishes rules for where library code must insert panic calls. If a
13421342
function is marked safe but is internally unsound for some values of its
13431343
arguments, it should check those arguments and panic before executing
1344-
the unsafe operation. Unsafe functions generally don’t panic because its
1345-
the responsibility of their callers to observe the preconditions of the
1346-
function.</p>
1344+
the unsafe operation. Unsafe functions generally don’t panic because
1345+
it’s the responsibility of their callers to observe the preconditions of
1346+
the function.</p>
13471347
<h1 data-number="2" id="design-overview"><span class="header-section-number">2</span> Design overview<a href="#design-overview" class="self-link"></a></h1>
13481348
<h2 data-number="2.1" id="the-safe-context"><span class="header-section-number">2.1</span> The
13491349
<code class="sourceCode cpp">safe</code> context<a href="#the-safe-context" class="self-link"></a></h2>
@@ -1892,7 +1892,7 @@ <h3 data-number="2.2.1" id="use-after-free"><span class="header-section-number">
18921892
<p><code class="sourceCode cpp">std<span class="op">::</span>string_view</code>
18931893
was added to C++17 as a safer alternative to passing character pointers
18941894
around. Unfortunately, its rvalue-reference constructor is so
1895-
dangerously designed that its reported to <em>encourage</em>
1895+
dangerously designed that it’s reported to <em>encourage</em>
18961896
use-after-free bugs.<span class="citation" data-cites="string-view-use-after-free">[<a href="https://github.com/isocpp/CppCoreGuidelines/issues/1038" role="doc-biblioref">string-view-use-after-free</a>]</span></p>
18971897
<p><a href="https://github.com/cppalliance/safe-cpp/blob/master/proposal/string_view0.cxx"><strong>string_view0.cxx</strong></a>
18981898
– <a href="https://godbolt.org/z/e3TG6W5Me">(Compiler Explorer)</a></p>
@@ -2560,7 +2560,7 @@ <h3 data-number="2.2.5" id="systems-of-constraints"><span class="header-section-
25602560
<p>I’ve relabelled the example to show function points and region names
25612561
of variables and loans. If we run live analysis on ’R0, the region for
25622562
the variable <code class="sourceCode cpp">ref</code>, we see it’s live
2563-
at points ’R0 = { 4, 8, 9, 10, 11 }. These are the points where its
2563+
at points ’R0 = { 4, 8, 9, 10, 11 }. These are the points where it’s
25642564
subsequently used. We’ll grow the loan regions ’R1 and ’R2 until their
25652565
constraint equations are satisfied.</p>
25662566
<p><code class="sourceCode cpp"><span class="ch">&#39;R</span><span class="er">1 : </span><span class="ch">&#39;</span>R0 <span class="op">@</span> P3</code>
@@ -2642,8 +2642,8 @@ <h3 data-number="2.2.6" id="lifetime-error-reporting"><span class="header-sectio
26422642
constraints could help users diagnose borrow checker errors. But there’s
26432643
a fine line between presenting an error like the one above, which is
26442644
already pretty wordy, and overwhelming programmers with information.</p>
2645-
<h3 data-number="2.2.7" id="lifetime-constraints-on-called-functinos"><span class="header-section-number">2.2.7</span> Lifetime constraints on
2646-
called functinos<a href="#lifetime-constraints-on-called-functinos" class="self-link"></a></h3>
2645+
<h3 data-number="2.2.7" id="lifetime-constraints-on-called-functions"><span class="header-section-number">2.2.7</span> Lifetime constraints on
2646+
called functions<a href="#lifetime-constraints-on-called-functions" class="self-link"></a></h3>
26472647
<p>Borrow checking is easiest to understand when applied to a single
26482648
function. The function is lowered to a control flow graph, the compiler
26492649
assigns regions to loans and borrow variables, emits lifetime
@@ -4343,8 +4343,8 @@ <h2 data-number="2.6" id="interior-mutability"><span class="header-section-numbe
43434343
const off shared borrows, allowing users to mutate the protected
43444344
resource.</p>
43454345
<p>Safe C++ and Rust conflate exclusive access with mutable borrows and
4346-
shared access with const borrows. It’s is an economical choice, because
4347-
one type qualifier,
4346+
shared access with const borrows. It’s an economical choice, because one
4347+
type qualifier,
43484348
<code class="sourceCode cpp"><span class="kw">const</span></code> or
43494349
<code class="sourceCode cpp">mut</code>, also determines exclusivity.
43504350
But the cast-away-const model of interior mutability is an awkward
@@ -4485,7 +4485,7 @@ <h2 data-number="2.7" id="send-and-sync"><span class="header-section-number">2.7
44854485
<code class="sourceCode cpp">T</code> is
44864486
<code class="sourceCode cpp">send</code>. Since most types are
44874487
<code class="sourceCode cpp">send</code> by construction, we can safely
4488-
mutate shared state over multiple threads as long as its wrapped in a
4488+
mutate shared state over multiple threads as long as it’s wrapped in a
44894489
<code class="sourceCode cpp">std2<span class="op">::</span>mutex</code>
44904490
and that’s owned by an
44914491
<code class="sourceCode cpp">std2<span class="op">::</span>arc</code>.
@@ -4798,7 +4798,7 @@ <h1 data-number="4" id="implementation-guidance"><span class="header-section-num
47984798
passes. The first thing compiler engineers should focus on when pursuing
47994799
memory safety is to lower their frontend’s AST to MIR. Several compiled
48004800
languages already pass through a mid-level IR: Swift passes through
4801-
SIL,<span class="citation" data-cites="sil">[<a href="https://github.com/swiftlang/swift/blob/main/docs/SIL.rst" role="doc-biblioref">sil</a>]</span> Rust passes through MIR,<span class="citation" data-cites="mir">[<a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" role="doc-biblioref">mir</a>]</span> and Circle passes through it’s
4801+
SIL,<span class="citation" data-cites="sil">[<a href="https://github.com/swiftlang/swift/blob/main/docs/SIL.rst" role="doc-biblioref">sil</a>]</span> Rust passes through MIR,<span class="citation" data-cites="mir">[<a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" role="doc-biblioref">mir</a>]</span> and Circle passes through its
48024802
mid-level IR when targeting the new object model. There is an effort
48034803
called ClangIR<span class="citation" data-cites="clangir">[<a href="https://discourse.llvm.org/t/rfc-upstreaming-clangir/76587" role="doc-biblioref">clangir</a>]</span> to lower Clang to an MLIR
48044804
dialect called CIR, but the project is in an early phase and doesn’t

proposal/draft.md

+8-8
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ The "billion-dollar mistake" is a type safety problem. Consider `std::unique_ptr
210210

211211
As Hoare observes, the problem comes from conflating two different things, a pointer to an object and an empty state, into the same type and giving them the same interface. Smart pointers should only hold valid pointers. Denying the null state eliminates undefined behavior.
212212

213-
We address the type safety problem by overhauling the object model. Safe C++ features a new kind of move: [_relocation_](#relocation-object-model), also called _destructive move_. The object model is called an _affine_ or a _linear_ type system. Unless explicitly initialized, objects start out _uninitialized_. They can't be used in this state. When you assign to an object, it becomes initialized. When you relocate from an object, it's value is moved and it's reset to uninitialized. If you relocate from an object inside control flow, it becomes _potentially uninitialized_, and its destructor is conditionally executed after reading a compiler-generated drop flag.
213+
We address the type safety problem by overhauling the object model. Safe C++ features a new kind of move: [_relocation_](#relocation-object-model), also called _destructive move_. The object model is called an _affine_ or a _linear_ type system. Unless explicitly initialized, objects start out _uninitialized_. They can't be used in this state. When you assign to an object, it becomes initialized. When you relocate from an object, its value is moved and it's reset to uninitialized. If you relocate from an object inside control flow, it becomes _potentially uninitialized_, and its destructor is conditionally executed after reading a compiler-generated drop flag.
214214

215215
`std2::box` is our version of `unique_ptr`. It has no null state. There's no default constructor. Dereference it without risk of undefined behavior. If this design is so much safer, why doesn't C++ simply introduce its own fixed `unique_ptr` without a null state? Blame C++11 move semantics.
216216

@@ -530,7 +530,7 @@ public:
530530
};
531531
```
532532
533-
The [safety model](#memory-safety-as-terms-and-conditions) establishes rules for where library code must insert panic calls. If a function is marked safe but is internally unsound for some values of its arguments, it should check those arguments and panic before executing the unsafe operation. Unsafe functions generally don't panic because its the responsibility of their callers to observe the preconditions of the function.
533+
The [safety model](#memory-safety-as-terms-and-conditions) establishes rules for where library code must insert panic calls. If a function is marked safe but is internally unsound for some values of its arguments, it should check those arguments and panic before executing the unsafe operation. Unsafe functions generally don't panic because it's the responsibility of their callers to observe the preconditions of the function.
534534
535535
# Design overview
536536
@@ -925,7 +925,7 @@ Garbage collection requires storing objects on the _heap_. But C++ is about _man
925925

926926
### Use-after-free
927927

928-
`std::string_view` was added to C++17 as a safer alternative to passing character pointers around. Unfortunately, its rvalue-reference constructor is so dangerously designed that its reported to _encourage_ use-after-free bugs.[@string-view-use-after-free]
928+
`std::string_view` was added to C++17 as a safer alternative to passing character pointers around. Unfortunately, its rvalue-reference constructor is so dangerously designed that it's reported to _encourage_ use-after-free bugs.[@string-view-use-after-free]
929929

930930
[**string_view0.cxx**](https://github.com/cppalliance/safe-cpp/blob/master/proposal/string_view0.cxx) -- [(Compiler Explorer)](https://godbolt.org/z/e3TG6W5Me)
931931
```cpp
@@ -1386,7 +1386,7 @@ P11: f(*ref);
13861386
}
13871387
```
13881388

1389-
I've relabelled the example to show function points and region names of variables and loans. If we run live analysis on 'R0, the region for the variable `ref`, we see it's live at points 'R0 = { 4, 8, 9, 10, 11 }. These are the points where its subsequently used. We'll grow the loan regions 'R1 and 'R2 until their constraint equations are satisfied.
1389+
I've relabelled the example to show function points and region names of variables and loans. If we run live analysis on 'R0, the region for the variable `ref`, we see it's live at points 'R0 = { 4, 8, 9, 10, 11 }. These are the points where it's subsequently used. We'll grow the loan regions 'R1 and 'R2 until their constraint equations are satisfied.
13901390

13911391
`'R1 : 'R0 @ P3` means that starting at P3, the 'R1 contains all points 'R0 does, along all control flow paths, as long as 'R0 is live. 'R1 = { 3, 4 }. Grow 'R2 the same way: 'R2 = { 7, 8, 9, 10, 11 }.
13921392

@@ -1440,7 +1440,7 @@ Circle tries to identify all three of these points when forming borrow checker e
14401440

14411441
The invariants that are tested are established with a network of lifetime constraints. It might not be the case that the invalidating action is obviously related to either the place of the loan or the use that extends the loan. More completely describing the chain of constraints could help users diagnose borrow checker errors. But there's a fine line between presenting an error like the one above, which is already pretty wordy, and overwhelming programmers with information.
14421442

1443-
### Lifetime constraints on called functinos
1443+
### Lifetime constraints on called functions
14441444

14451445
Borrow checking is easiest to understand when applied to a single function. The function is lowered to a control flow graph, the compiler assigns regions to loans and borrow variables, emits lifetime constraints where there are assignments, iteratively grows regions until the constraints are solved, and walks the instructions, checking for invalidating actions on loans in scope. Within the definition of the function, there's nothing it can't analyze. The complexity arises when passing and receiving borrows through function calls.
14461446

@@ -2555,7 +2555,7 @@ Lifetime safety also guarantees that the `lock_guard` is in scope (meaning the m
25552555
25562556
Interior mutability is a legal loophole around exclusivity. You're still limited to one mutable borrow or any number of shared borrows to an object. Types with a deconfliction strategy use `unsafe_cell` to safely strip the const off shared borrows, allowing users to mutate the protected resource.
25572557
2558-
Safe C++ and Rust conflate exclusive access with mutable borrows and shared access with const borrows. It's is an economical choice, because one type qualifier, `const` or `mut`, also determines exclusivity. But the cast-away-const model of interior mutability is an awkward consequence. This design may not be the only way: The Ante language[@ante] experiments with separate `own mut` and `shared mut` qualifiers. That's really attractive, because you're never mutating something through a const reference. This three-state system doesn't map onto C++'s existing type system as easily, but that doesn't mean the const/mutable borrow treatment, which does integrate elegantly, is the most expressive. A `shared` type qualifier merits investigation during the course of this project.
2558+
Safe C++ and Rust conflate exclusive access with mutable borrows and shared access with const borrows. It's an economical choice, because one type qualifier, `const` or `mut`, also determines exclusivity. But the cast-away-const model of interior mutability is an awkward consequence. This design may not be the only way: The Ante language[@ante] experiments with separate `own mut` and `shared mut` qualifiers. That's really attractive, because you're never mutating something through a const reference. This three-state system doesn't map onto C++'s existing type system as easily, but that doesn't mean the const/mutable borrow treatment, which does integrate elegantly, is the most expressive. A `shared` type qualifier merits investigation during the course of this project.
25592559
25602560
* `T^` - Exclusive mutable access. Permits standard conversion to `shared T^` and `const T^`.
25612561
* `shared T^` - Shared mutable access. Permits standard conversion to `const T^`. Only types that enforce interior mutability have overloads with shared mutable access.
@@ -2610,7 +2610,7 @@ class [[
26102610

26112611
`std2::mutex` is another candidate for use with `std2::arc`. This type is thread safe. As shown in the [thread safety](#thread-safety) example, it provides threads with exclusive access to its interior data using a synchronization object. The borrow checker prevents the reference to the inner data from being used outside of the mutex's lock. Therefore, `std2::mutex` is `sync` if its inner type is `send`. Why make it conditional on `send` when the mutex is already providing threads with exclusive access to the inner value? This provides protection for the rare type with thread affinity. A type is `send` if it can both be copied to a different thread _and used_ by a different thread.
26122612

2613-
`std2::arc<std2::mutex<T>>` is `send` if `std2::mutex<T>` is `send` and `sync`. `std2::mutex<T>` is `send` and `sync` if `T` is `send`. Since most types are `send` by construction, we can safely mutate shared state over multiple threads as long as its wrapped in a `std2::mutex` and that's owned by an `std2::arc`. The `arc` provides shared ownership. The `mutex` provides shared mutation.
2613+
`std2::arc<std2::mutex<T>>` is `send` if `std2::mutex<T>` is `send` and `sync`. `std2::mutex<T>` is `send` and `sync` if `T` is `send`. Since most types are `send` by construction, we can safely mutate shared state over multiple threads as long as it's wrapped in a `std2::mutex` and that's owned by an `std2::arc`. The `arc` provides shared ownership. The `mutex` provides shared mutation.
26142614

26152615
```cpp
26162616
class thread {
@@ -2773,7 +2773,7 @@ We should also revise the policy for using lifetime parameters in class definiti
27732773

27742774
# Implementation guidance
27752775

2776-
The intelligence behind the _ownership and borrowing_ safety model resides in the compiler's middle-end, in its _MIR analysis_ passes. The first thing compiler engineers should focus on when pursuing memory safety is to lower their frontend's AST to MIR. Several compiled languages already pass through a mid-level IR: Swift passes through SIL,[@sil] Rust passes through MIR,[@mir] and Circle passes through it's mid-level IR when targeting the new object model. There is an effort called ClangIR[@clangir] to lower Clang to an MLIR dialect called CIR, but the project is in an early phase and doesn't have enough coverage to support the language or library features described in this document.
2776+
The intelligence behind the _ownership and borrowing_ safety model resides in the compiler's middle-end, in its _MIR analysis_ passes. The first thing compiler engineers should focus on when pursuing memory safety is to lower their frontend's AST to MIR. Several compiled languages already pass through a mid-level IR: Swift passes through SIL,[@sil] Rust passes through MIR,[@mir] and Circle passes through its mid-level IR when targeting the new object model. There is an effort called ClangIR[@clangir] to lower Clang to an MLIR dialect called CIR, but the project is in an early phase and doesn't have enough coverage to support the language or library features described in this document.
27772777

27782778
The AST->MIR and MIR->LLVM pipelines (or whatever codegen is used) fully replaces the compiler's old AST->LLVM codegen. It is more difficult to lower through MIR than directly emitting LLVM, but implementing new codegen is not a very large investment. You can look into Circle's MIR support with the `-print-mir` and `-print-mir-drop` cmdline options, which print the MIR before and after drop elaboration, respectively.
27792779

0 commit comments

Comments
 (0)