Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It's not orthogonal at all. Nontrivial mutations to multi-word sized values are inherently racy operations that can lead to UB if you don't have restrictions on how value types can be used. D isn't memory safe; Active Oberon employs mandatory object locking on writes (which is a rather heavy runtime solution); Cedar values are either immutable, or always valid with respect to word sized mutations (as far as I can tell, unions, for example, can only exist behind a pointer indirection). Modula 3 objects are not, as far as I can tell, value types (i.e. they must be heap-allocated and mutation creates a new record), and those are the only types it has where a "tearing" mutation could invalidate the runtime representation.

So, I disagree: all of the languages you mentioned either are memory unsafe when you use multiword value types concurrently, have one of the restrictions I mentioned, or (in the case of Active Oberon) conservatively lock on all writes. These value types are not "unrestricted" compared to primitives, all of which can be mutated in-place without allocating or locking, or to heap objects, which can be updated atomically even for interesting values at the cost of an allocation. Moreover, the issue is intimately tied to GC, or more accurately to the programmer not having to explicitly keep track of whether objects are uniquely owned or not (which is generally the function of a GC). As a result, if garbage collected objects can hold references to nontrivial value types, there is almost no way to avoid this problem.



I don't get why you are jumping into memory safety in multi-threaded environments.

We are discussing GC enabled systems programming languages with support for unsafe operations, not Rust's idea of thread safety.

D is memory safe in the context of @safe code, of course @system code is unsafe.

Active Oberon and Modula-3 also have record types, strings and arrays that can be stack allocated, declared on the global data memory segment or just manually allocated in unsafe packages.

In both cases I can also declare untraced pointers for memory blocks in unsafe packages, that the GC will gladly ignore.


Lack of thread safety leads to undefined behavior when you have nontrivial mutations to value types with tearing. That has nothing to do with some abstract notion of thread safety, nor does it have anything to do with Rust. It's a very straightforward consequence of the failure of (for example) things like bounds checking, or interpreting an integer as a pointer.

Record types with fields that don't depend on one another, strings and (fixed-size) arrays only support "uninteresting" mutations that remain valid even in the presence of atomic tearing. They are restricted compared to the general value types that you can have in a language like C++ (for example, sum types, unions, "fat objects" like Go's interfaces, or vectors with dynamic lengths), because for the latter having a write tear can lead to undefined behavior (for instance, overwriting a value vector can lead to the pointed-to vector part temporarily having a different length than the length field would indicate, which can easily lead to a buffer overflow).

The memory safe fragment of D has similar restrictions on its value types to the above. For example, its value arrays must have a size known at compile time.

Of course you can support such features in the unsafe fragment of a language. However, this is a pretty significant deterrent to actually using the feature and idiomatic code will avoid it most of the time.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: