profile
viewpoint

japaric/xargo 788

The sysroot manager that lets you build and customize `std`

Gilnaa/memoffset 84

offsetof for Rust

opatut/dudel 29

This used to be a webapp for scheduling meetings easily. Now it's no longer maintained. Have a look at Bitpoll instead:

Diggsey/rust-field-offset 23

Safe pointer-to-member functionality for rust

RalfJung/ansible 1

ansible playbooks for my servers

freifunk-saar/tunneldigger 0

Client and broker for our custom L2TPv3 NAT-traversing tunnel setup protocol based on L2TPv3 support in the Linux kernel.

hacksaar/Firmware 0

ESP32 firmware for the SHA2017 badge

hacksaar/micropython-esp32 0

MicroPython ported to the SHA2017 badge

issue commentcoq/coq

Add perennial to Coq benchmarks

I am not sure if @tchajed wants Perennial to be available in a general opam repository; the usual way to use it is to work directly with the git repository.

RalfJung

comment created time in an hour

issue commentcoq/coq

Add perennial to Coq benchmarks

I do not think there is an opam file, but I could write one if that is required. Does it have to be in an opam repository or can you directly pin the git repo?

RalfJung

comment created time in 3 hours

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

@herbelin that reflects my original proposal, albeit with way more detail than I could have supplied. :)

RalfJung

comment created time in 5 hours

issue openedcoq/coq

Add perennial to Coq benchmarks

Description of the problem

In https://github.com/coq/coq/pull/12914, @ppedrot asked if there were other representative Iris-based benchmarks we could add besides lambda-rust. @tchajed then suggested Perennial, which I would not call representative, but it is certainly stress-testing Iris and has lots of slow proofs, so is probably a reasonable performance test. However, it is quite slow to build, taking more than 1h.

Would the Coq team be okay with adding Perennial to its benchmarks, and what would that take?

created time in 5 hours

push eventRalfJung/rust

Ralf Jung

commit sha 153e843c490d569f4faf8f2e625dc7b837df584f

fix Rvalue::ty for ThreadLocalRef

view details

push time in 6 hours

issue commentrust-lang/rust

simd_insert and simd_extract allow garbage data

https://github.com/rust-lang/rust/issues/70271 is somewhat related.

workingjubilee

comment created time in 6 hours

pull request commentrust-lang/rust

fix static_ptr_ty for foreign statics

I think I found where the wrong type was coming from.

RalfJung

comment created time in 7 hours

push eventRalfJung/rust

Ralf Jung

commit sha ed54d959fe376df0785ee2c1eef3f738a9407476

fix Rvalue::ty for ThreadLocalRef

view details

push time in 7 hours

pull request commentrust-lang/rust

fix static_ptr_ty for foreign statics

Or maybe it is the other way around -- convert_path_expr now uses raw pointers for extern thread-local refs but something else still expects that to be a normal reference. Thread-local refs have their own ExprKind and I don't know how that works.

RalfJung

comment created time in 7 hours

pull request commentrust-lang/rust

fix static_ptr_ty for foreign statics

However, the test suite currently fails with an ICE:

---- [ui] ui/threads-sendsync/thread-local-extern-static.rs stdout ----

error: test compilation failed although it shouldn't!
status: exit code: 101
command: "/home/r/src/rust/rustc.2/build/x86_64-unknown-linux-gnu/stage1/bin/rustc" "/home/r/src/rust/rustc.2/src/test/ui/threads-sendsync/thread-local-extern-static.rs" "-Zthreads=1" "--target=x86_64-unknown-linux-gnu" "--error-format" "json" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "-C" "prefer-dynamic" "-o" "/home/r/src/rust/rustc.2/build/x86_64-unknown-linux-gnu/test/ui/threads-sendsync/thread-local-extern-static/a" "-Crpath" "-O" "-Cdebuginfo=0" "-Zunstable-options" "-Lnative=/home/r/src/rust/rustc.2/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/home/r/src/rust/rustc.2/build/x86_64-unknown-linux-gnu/test/ui/threads-sendsync/thread-local-extern-static/auxiliary"
stdout:
------------------------------------------

------------------------------------------
stderr:
------------------------------------------
error: internal compiler error: broken MIR in DefId(0:7 ~ thread_local_extern_static[317d]::main) (_6 = &/*tls*/ FOO): bad assignment (*const std::cell::Cell<u32> = &'static std::cell::Cell<u32>): NoSolution
  --> /home/r/src/rust/rustc.2/src/test/ui/threads-sendsync/thread-local-extern-static.rs:22:20
   |
LL |         assert_eq!(FOO.get(), 3);
   |                    ^^^
   |
   = note: delayed at compiler/rustc_mir/src/borrow_check/type_check/mod.rs:253:27

error: internal compiler error: broken MIR in Item(WithOptConstParam { did: DefId(0:7 ~ thread_local_extern_static[317d]::main), const_param_did: None }) (end of phase Optimization) at bb0[5]:
encountered `Assign((_5, &/*tls*/ FOO))` with incompatible types:
left-hand side has type: *const Cell<u32>
right-hand side has type: &'static Cell<u32>
  --> /home/r/src/rust/rustc.2/src/test/ui/threads-sendsync/thread-local-extern-static.rs:22:20
   |
LL |         assert_eq!(FOO.get(), 3);
   |                    ^^^
   |
   = note: delayed at compiler/rustc_mir/src/transform/validate.rs:156:36

Looks like something somewhere is not using static_ptr_ty to determine the type of a thread-local extern static and thus causes different types to be used on the two sides.

RalfJung

comment created time in 8 hours

PR opened rust-lang/rust

fix static_ptr_ty for foreign statics

Cc https://github.com/rust-lang/rust/issues/74840

This does not fix that issue but fixes a problem in static_ptr_ty that we noticed while discussing that issue. I also added and updated a few comments. The one about internal locals being ignored does not seem to have been true even in the commit that introduced it.

r? @oli-obk

+6 -3

0 comment

3 changed files

pr created time in 8 hours

create barnchRalfJung/rust

branch : foreign-static

created branch time in 8 hours

Pull request review commentrust-lang/rust

consider assignments of union field of ManuallyDrop type safe

 impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {                     UnsafetyViolationKind::GeneralAndConstFn,                     UnsafetyViolationDetails::DerefOfRawPointer,                 ),-                ty::Adt(adt, _) => {-                    if adt.is_union() {-                        if context == PlaceContext::MutatingUse(MutatingUseContext::Store)-                            || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)-                            || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)-                        {-                            let elem_ty = match elem {-                                ProjectionElem::Field(_, ty) => ty,-                                _ => span_bug!(-                                    self.source_info.span,-                                    "non-field projection {:?} from union?",-                                    place-                                ),-                            };-                            if !elem_ty.is_copy_modulo_regions(+                ty::Adt(adt, _) if adt.is_union() => {+                    if context == PlaceContext::MutatingUse(MutatingUseContext::Store)+                        || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)+                        || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)+                    {+                        let elem_ty = match elem {+                            ProjectionElem::Field(_, ty) => ty,+                            _ => span_bug!(+                                self.source_info.span,+                                "non-field projection {:?} from union?",+                                place+                            ),+                        };+                        let manually_drop = elem_ty+                            .ty_adt_def()+                            .map_or(false, |adt_def| adt_def.is_manually_drop());

This leads to the funny situation where, inside core::mem::manually_drop, unions are unsound, because we can do things like

union U {
  f: ManuallyDrop<Vec<i32>>,
}

fn test(u: U) {
  u.f.value = Vec::new();
}

It's a bit awkward to be checking the type of the field here when really we care about the type of the assignment.

RalfJung

comment created time in 8 hours

PullRequestReviewEvent

Pull request review commentrust-lang/rust

consider assignments of union field of ManuallyDrop type safe

 impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {                     UnsafetyViolationKind::GeneralAndConstFn,                     UnsafetyViolationDetails::DerefOfRawPointer,                 ),-                ty::Adt(adt, _) => {-                    if adt.is_union() {-                        if context == PlaceContext::MutatingUse(MutatingUseContext::Store)-                            || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)-                            || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)-                        {-                            let elem_ty = match elem {-                                ProjectionElem::Field(_, ty) => ty,-                                _ => span_bug!(-                                    self.source_info.span,-                                    "non-field projection {:?} from union?",-                                    place-                                ),-                            };-                            if !elem_ty.is_copy_modulo_regions(+                ty::Adt(adt, _) if adt.is_union() => {+                    if context == PlaceContext::MutatingUse(MutatingUseContext::Store)+                        || context == PlaceContext::MutatingUse(MutatingUseContext::Drop)+                        || context == PlaceContext::MutatingUse(MutatingUseContext::AsmOutput)

What slightly concerns me here is that this is the context of the entire place, but we are considering here just a single place projection. What if there is a deref involved?

#![feature(untagged_unions)]

union U {
    p: &'static mut i32,
}

fn test(u: U) {
    *(u.p) = 13;
}

This does error, but the error claims "assignment to non-Copy union field", which is not really what happens (the value of p is not changed). The problem is that this is reading a union field. If I made the test here even smarter to take into account needs_drop, then that code above would be incorrectly accepted!

In fact, union fields that drop are not even permitted any more (with no amount of feature gates).

RalfJung

comment created time in 8 hours

PullRequestReviewEvent

delete branch RalfJung/reference

delete branch : union

delete time in 17 hours

issue commentocaml/opam-repository

Confusing state of num in 4.05.0 and ocamlfind 1.7.3

AFAIK this is still a problem.

rleonid

comment created time in 20 hours

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

That's at the code level, but AFAIK it still remains the case that lifetimes still correspond to a contiguous temporal region in the execution trace of the program. In particular, a lifetime can be said to "end" in SB when some/all of the relevant borrow stack items are popped due to an access further up the stack. Hence my earlier attempt to use this to trigger a write-back effect, as this is the point at which up stack references become able to access the memory again.

The problem is that there is this weird time when some of the relevant items have been removed but others still exist.

I guess what I am saying is, there's a lot of unknowns here and things that would need exploring. I personally think this is too complex compared to the optimizations it justifies, so I am unlikely to be doing that exploring. But if someone else figures out all the details, we'd certainly learn something from that. :)

RalfJung

comment created time in 20 hours

issue commentrust-lang/unsafe-code-guidelines

Meaning of Undefined and Justification for UB

Without having time right now to respond to all the points:

In general, yes, you shouldn't invoke undefined behaviour, but sometimes (especially when writing libraries to support the specification/standard) it can become unavoidable.

If the Rust standard library ever invokes UB, that is a critical bug -- please report it if you find such a case. it is certainly avoidable to do so, and it is a huge problem if we do so for all the usual reasons that UB is bad.

It is true that some of our docs are imprecise about the distinction between UB and unspecified behavior, and also sometimes about the distinction between library-level UB and language-level UB. I am trying to fix such cases as I see them.

chorman0773

comment created time in 20 hours

push eventRalfJung/reference

Ralf Jung

commit sha 0d6f579859c875371c8cfdd6314bb23b19c7a2e8

add ManuallyDrop link Co-authored-by: Eric Huss <eric@huss.org>

view details

push time in 21 hours

push eventRalfJung/reference

Ralf Jung

commit sha e43960659a87d1a25a844f21ca17a38b7bb58323

add ManuallyDrop link Co-authored-by: Eric Huss <eric@huss.org>

view details

push time in 21 hours

pull request commentrust-lang/reference

mention how unions interact with dropping

Am I correct to say that before rust-lang/rust#77547 that all fields were required to be Copy? If so, then my confusion was that it said write to a field that doesn't implement Copy, buy why mention "Copy" if it wasn't possible to define a non-Copy field in the first place?

You are correct for stable Rust. The docs were likely written by someone who thought of what is possible when enabling nightly feature gates.

RalfJung

comment created time in 21 hours

issue commentrust-lang/unsafe-code-guidelines

Meaning of Undefined and Justification for UB

Does rust have a category corresponding to C/C++'s "implementation defined" then? It sounds like we would want to avoid it, and as long as "rust = rustc" it's a bit difficult to distinguish implementation defined from plain old defined behavior.

Not yet, mostly for the reasons you mentioned. I think such questions will come up way later in the process.

The IMO more interesting other "kind of behavior" to talk about is unspecified behavior, which is closely related. There was an attempt to define it that failed to reach consensus. (That PR should likely be closed and a new one started.) The only real difference between "unspecified" and "implementation-defined" is that for the latter, implementations need to document which choice they make -- so once we nailed down what "unspecified behavior" means, we pretty much also covered "implementation-defined", we just need to decide on a case-by-case basis if implementantions ought to document a choice (and guarantee that choice for future versions of the implementation) or not.

chorman0773

comment created time in a day

issue commentrust-lang/unsafe-code-guidelines

Meaning of Undefined and Justification for UB

One point, I've noticed that UB has to be justified by the optimizations it enables. I would add that undefined behaviour was never intended to be a key to optimizations, it just happens that as a result of it's definition

Historically, UB might not have started as being primarily for optimizations, but over the last few decades that is certainly the case. To give one example, strict aliasing in C has only one purpose: more optimizations.

In Rust, without the historical luggage of C/C++, we use UB only for optimizations. There are better ways to handle platform differences, as @Diggsey mentioned. For example, we have little-endian and big-endian platforms, and this is handled by having an explicit parameter in the Rust Abstract machine defining endianess. So it is not UB to do something byte-level with multi-byte integer types, but results differ per platform. Such differences should obviously be kept to a minimum to make code maximally portable, but there can be good reasons to introduce them. Likewise, integer overflows are defined to either raise a panic or produce 2's-complement overflowing results (and in practice this is controlled by compiler flags). In such cases it is important to precisely specify what all the possible cases are, so that programmers can make their code correct wrt. all Rust implementations. This is what sets such platform differences apart from UB.

In particular this definition permits an implementation which assigns some meaning to undefined behaviour

A compiler implementation could specify what happens for some subset of programs which have UB according to the Rust language. However, this is out of scope when it comes to specifying Rust itelf, and it does not mean that the program itself becomes valid Rust.

To add to this, the purpose of the UCG (unsafe-code-guidelines WG) is to specify Rust, not to specify a particular Rust implementation. Basically, the long-term goal of the UCG is to produce something akin to (but better than ;) the C/C++ spec. As far as the spec and there the UCG is concerned, programs with UB are just wrong, period. This is the same as in C/C++.

Some members of the lang team have also expressed a preference in the past of not making any extra promises in rustc [the implementation] for things that are UB in Rust [the language]. They want to avoid fragmenting the language into dialects that only work with some implementations. Worse, since there is only one implementation, there is a huge risk of any such promise becoming a de-facto guarantee that the entire ecosystem relies on.

Therefore, as far as the rust-lang organization is concerned, programs with UB are beyond salvaging. They are not subject to stability guarantees (or any guarantees really) and they need to be fixed. Implementations could assign meaning to UB programs, but rustc [the implementation] does not, and it would be healthier for the ecosystem if alternative implementations do not do so, either, since any such guarantee is an ecosystem split.

In practice, rustc [the implementation] will do what it can to help programmers even if their programs have UB, if it does not compromise UB-free programs. Usually the goal here is to make the programmer aware of that problem so that they can fix their code. Sometimes we even temporarily take back changes that "break" UB programs until UB-free ways to do things are possible; this happened around unwinding for extern functions. (I put "break" in quote because with my spec hat on, UB programs are already broken, the compiler did not do anything wrong.) We do not just ignore the needs of users that have UB in their code, but the goal of that conversation is always to find a non-UB way for them to do what they need to do. None of this really changes the fundamental stanza on UB, in particular as far as the spec is concerned.

chorman0773

comment created time in a day

issue commentrust-lang/unsafe-code-guidelines

Meaning of Undefined and Justification for UB

Thank you for moving this discussion to a separate thread!

Note that UB is also defined in our glossary. This definition coincides with how modern C/C++ compilers interpret UB in their respective languages.

There is also an excellent blog post by Raph Levien that goes a bit into the history of UB. According to that post, UB in C/C++ used to be more about "we do not want to restrict what hardware does" than about enabling optimizations, but this meaning has shifted over time. In my opinion, UB is a terrible word for how the term is used today, I think something like "language contract" or so is much clearer, but I'm afraid we are probably stuck with it. The concept itself however is great: it is a way for the programmer to convey information to the compiler that the compiler would have no way to infer itself. However, problems arise when the programmer does not realize what information they are conveying. This happens a lot in C/C++ (when a programmer writes + they might not want to convey "I carefully checked that this will not overflow"), and also happens in Rust with some of the more subtle UB, in particular around validity and aliasing.

chorman0773

comment created time in a day

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

When the lifetime of a mutable reference ends,

As I have laid out above, this is not a very well-defined notion -- and to my knowledge, with Polonious, there is not even such a thing as "the point where a lifetime ends". With Polonious, a lifetime is not some region of code, but some set of memory locations a reference could point to, and such sets do not have an "end".

RalfJung

comment created time in a day

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

&mut -> *mut -> usize -> *mut -> &mut happens all over the place in unsafe code.

Indeed, and that's why -- quoting from my previous message -- "when a reference is cast to a pointer, the relevant memory gets marked as "accessible to pointers with unknown provenance"".

So this is UB?

It is UB if it forgets provenance. But then really you should not call it identity. If, on the Abstract Machine level, it truly is an identity function, then provenance is preserved.

Defining precisely how FFI works is really complicated (and I am not an expert, I know just enough to avoid the topic), so I don't think we want to go there. I also don't see how it helps the discussion. If you want to talk about linking and FFI precisely, you need a shared memory model of the two sides that get linked -- so if the linking happens while provenance still exists, both sides need to be aware that there is provenance, and define what happens to it. If the linking happens on the assembly level, there is no provenance and thus no provenance-related problems.

mahkoh

comment created time in a day

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

The dead store cannot be removed from the IR, but at some point you're going to be taking the IR to actual machine code, at which point those stores can be removed. (Since provenance doesn't actually exist on real hardware)

Sure, but that could still be a problem for later optimization passes on the IR -- "this piece of code does not write to memory" is a useful property.

 If x is an integer then turning x+0 into x is fine, because storing the integer result would still delete any provenance information at that memory location.

As @digama0 already said -- but what if I transmute the integer to a pointer? Then transmute(x) would have provenance but transmute(x+0) would not.

Surely as soon as FFI is involved you're going to have to deal with pointers whose provenance it unknown. For example, let's say there's a C function identity(ptr) that just returns the pointer you passed in, but which the compiler has no insight into.

Sure. In current Stacked Borrows, when a reference is cast to a pointer, the relevant memory gets marked as "accessible to pointers with unknown provenance". I hope to move this change to ptr-to-int casts at some point which probably better models LLVM behavior, but as this discussion shows, LLVM behavior is unclear.

It is absolutely imperative that pointers with unknown provenance can not access memory that an &mut points to -- every aliasing-based optimization needs this. But from this it follows that if we just forget the provenance of an &mut, we will definitely have UB.

mahkoh

comment created time in a day

Pull request review commentrust-lang/rust

Cleanup constant matching in exhaustiveness checking

 fn pat_constructor<'tcx>(             if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {                 Some(IntRange(int_range))             } else {-                match (value.val, &value.ty.kind()) {-                    (_, ty::Array(_, n)) => {-                        let len = n.eval_usize(tcx, param_env);-                        Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))-                    }-                    (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => {-                        let len = (end - start) as u64;-                        Some(Slice(Slice { array_len: None, kind: FixedLen(len) }))-                    }-                    // FIXME(oli-obk): implement `deref` for `ConstValue`-                    // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... }-                    _ => Some(ConstantValue(value)),+                match value.ty.kind() {+                    ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),+                    ty::Ref(_, t, _) if t.is_str() => Some(Str(value)),+                    _ => Some(Opaque),

Maybe add a comment explaining that we assume that all constants that should count towards making a match exhaustive have already been turned into more specific Pat before by const_to_pat?

Nadrieril

comment created time in a day

PullRequestReviewEvent

PR opened rust-lang/rust

we can test std and core panic macros together

r? @oli-obk

+90 -70

0 comment

6 changed files

pr created time in a day

push eventRalfJung/rust

Ralf Jung

commit sha 762ded17f83a9309c5d8a626e2bc30a544048007

we can test std and core panic macros together

view details

push time in a day

pull request commentrust-lang/rust

Fix const core::panic!(non_literal_str).

Could you please add a test? The right file would be src/test/ui/consts/const-eval/const_panic.rs (unfortunately I have a parallel PR there so we'll conflict...)

m-ou-se

comment created time in a day

create barnchRalfJung/rust

branch : const-panic-test

created branch time in a day

pull request commentrust-lang/rust

Fix const core::panic!(non_literal_str).

Thanks for the PR! Cc @rust-lang/wg-const-eval

m-ou-se

comment created time in a day

pull request commenttokio-rs/bytes

Switch `BufMut::bytes_mut` from `MaybeUninit` to `&mut UninitSlice`, a type ownec by `bytes` for this purpose

cc @RalfJung this fix our unsoundness? :)

I have not reviewed your entire BufMut API surface, it is a bit too big for that.^^ But this seems to plug the hole that is caused by MaybeUninit not protecting against "de-initialization". (That was never its intended goal, IMO it is reasonable to expect such things to be done on types layered on top of MaybeUninit, like you did here.)

carllerche

comment created time in a day

Pull request review commenttokio-rs/bytes

Switch `BufMut::bytes_mut` from `MaybeUninit` to `&mut UninitSlice`, a type ownec by `bytes` for this purpose

+use core::fmt;+use core::mem::MaybeUninit;+use core::ops::{+    Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,+};++/// Uninititialized byte slice.+///+/// Returned by `BufMut::bytes_mut()`, the referenced byte slice may be+/// uninitialized. The wrapper provides safe access without introducing+/// undefined behavior.

It would be good to say what the concrete invariant/protocol for this type is, and in particular how it differs from MaybeUninit.

if I understand correctly, the key difference is that the contents may be uninitialized, but the API ensures that once they are initialized, they will never be de-initialized again. Is that correct?

carllerche

comment created time in a day

PullRequestReviewEvent

PR opened rust-lang/rust

consider assignments of union field of ManuallyDrop type safe

Assigning to Copy union fields is safe because that assignment will never drop anything. However, with https://github.com/rust-lang/rust/pull/77547, unions may also have ManuallyDrop fields, and their assignments are currently still unsafe. That seems unnecessary though, as assigning ManuallyDrop does not drop anything either, and is thus safe even for union fields.

I assume this will require FCP.

+35 -56

0 comment

4 changed files

pr created time in a day

push eventRalfJung/rust

Ralf Jung

commit sha e23915f66ef521d1fdd201cd563174836e7469a1

consider assignments of union field of ManuallyDrop type safe

view details

push time in a day

create barnchRalfJung/rust

branch : union-safe-assign

created branch time in a day

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

To be able to give meaning to the dependency of x in the conclusion (and to an in |- * clause), one would need a way to invert S so that x is rephrased in term of the action of the pattern, i.e. in terms of S x (which for S is possible, leading to x : nat |- pred x = pred x, but which is not generally possible).

I'm afraid this is way over my head. I have never seen in |- * clauses before and I do not know what you mean by "a dependent form of "apply S in x"". I looked up in and it seems to control which parts of the goal are adjusted by the case distinction; I do not understand how that would have interesting interactions with top-level-injection patterns.

Your code example should behave the same as

Theorem foo (x:unit * nat) : x = x.
Proof.
destruct x as [_ []%S] in |- *.

which is syntactically accepted but then fails saying

Cannot change n, it is used in conclusion.
RalfJung

comment created time in a day

delete branch RalfJung/rust

delete branch : miri

delete time in a day

pull request commentrust-lang/rust

stabilize union with 'ManuallyDrop' fields and 'impl Drop for Union'

However, that seems unnecessary for ManuallyDrop fields... I should probably submit a PR to make their assignment safe as well.

RalfJung

comment created time in a day

PR opened rust-lang/reference

mention how unions interact with dropping

Cc https://github.com/rust-lang/rust/pull/77547 r? @ehuss

+11 -1

0 comment

2 changed files

pr created time in a day

create barnchRalfJung/reference

branch : union

created branch time in a day

push eventRalfJung/reference

Ralf Jung

commit sha 0c8df59ff60f341ff0cf4d766a7c442b4a5837a4

mention how unions interact with dropping

view details

push time in a day

pull request commentrust-lang/rust

stabilize union with 'ManuallyDrop' fields and 'impl Drop for Union'

confusingly, it seems to imply that non-Copy fields can be used with unsafe).

No, it says that writing to Copy fields is safe.

Since transmutes can cause unexpected or undefined behaviour, unsafe is required to read from a union field or to write to a field that doesn't implement [Copy]

RalfJung

comment created time in a day

delete branch RalfJung/miri

delete branch : available-concurrency

delete time in a day

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

Question: It is not clear to me from this discussion whether it is part of the safety invariant that code cannot change a Some(t) to a None through the &mut T reference.

The safety invariant is an invariant, it does not talk about "how you can change a value". But I understand what you mean and thanks for asking.

The underlying question is, what exactly is the safety contract that all safe code will uphold, and that unsafe code can both rely on and must make sure to uphold to its clients. This is really hard to make precise; to my knowledge there has been exactly one attempt of this so far, and you can read all about it in my PhD thesis.

However, there is no reason (and I think it would be rather ill-advised, as I argued before) to make that contract identical with UB. And while making the contract precise will likely require very fancy logical machinery of the kind I use in my thesis, UB can and should be defined much simpler.

That's why I am so keen on not mixing up discussions about the safety contract and UB. For the safety contract we are limited to informal, vague discussions if we do not want to require people to take PhD-level courses on program logics. For UB I think we can have precise technical discussions without requiring quite so much background.

RalfJung

comment created time in a day

pull request commentrust-lang/miri

test new available_concurrency function

@bors r+

RalfJung

comment created time in a day

create barnchRalfJung/miri

branch : available-concurrency

created branch time in a day

PR opened rust-lang/miri

test new available_concurrency function

Cc https://github.com/rust-lang/rust/pull/74480

+6 -1

0 comment

2 changed files

pr created time in a day

pull request commentrust-lang/rust

stabilize union with 'ManuallyDrop' fields and 'impl Drop for Union'

@ehuss I suppose yes this should be documented -- I am not very fimiliar with how the reference is maintained.

RalfJung

comment created time in a day

issue commentrust-lang/rust

Must a `const fn` behave exactly the same at runtime as at compile-time?

Ah okay. Yes that's the plan, similar to how const fn can be called both from const- and non-const contexts.

oli-obk

comment created time in a day

Pull request review commentwlanslovenija/tunneldigger

Update server.rst

 Also the following Debian packages are required: * ``python-dev`` * ``libevent-dev`` +Debian 10 required following packages:+* ``iproute2``+* ``bridge-utils``+* ``python-dev``+* ``libevent-dev``+* ``libnetfilter-conntrack-dev``

This packet is not required any more as far as I know -- we do not have it installed on our servers and tunneldigger works fine there.

peterschristoph

comment created time in a day

PullRequestReviewEvent

Pull request review commentwlanslovenija/tunneldigger

Update server.rst

 Also the following Debian packages are required: * ``python-dev`` * ``libevent-dev`` +Debian 10 required following packages:

If we give a distro here, we should also give a distro above -- or replace the above list.

peterschristoph

comment created time in a day

PullRequestReviewEvent

Pull request review commentwlanslovenija/tunneldigger

Update server.rst

 If we assume that you are installing Tunneldigger under ``/srv/tunneldigger``     virtualenv -p /usr/bin/python3 env_tunneldigger  .. note::-    Tunneldigger only supports Python 3.+    Latest Tunneldigger only supports Python 3.

I don't think docs should retrace the history of all things that used to be supported in the past... everything the docs say only applies to latest tunneldigger. It would be more confusing to emphasize this here as that might be read to imply that some of the rest of the docs also apply to older tunneldigger, which they do not.

peterschristoph

comment created time in a day

PullRequestReviewEvent

issue commentrust-lang/rust

Must a `const fn` behave exactly the same at runtime as at compile-time?

I don't understand what you mean by "vary a given Trait impl for two types based on constness", but you might be referring to https://github.com/rust-lang/rfcs/pull/2632.

oli-obk

comment created time in a day

delete branch RalfJung/miri

delete branch : rustup

delete time in a day

delete branch RalfJung/miri

delete branch : readme

delete time in a day

pull request commentrust-lang/rust

Note that `BasicBlock` is just an index

Network failure. @bors retry

camelid

comment created time in a day

Pull request review commentrust-lang/rust

Note that `BasicBlock` is just an index

 rustc_index::newtype_index! {     /// are edges that go from a multi-successor node to a multi-predecessor node. This pass is     /// needed because some analyses require that there are no critical edges in the CFG.     ///+    /// Note that this type is just an index into [`Body.basic_blocks`](Body::basic_blocks);

Ah I see, thanks for clarifying.

I don't think there are other lists this is an index into, but you can audit all IndexVec<BasicBlock, _> if you want to be sure.

camelid

comment created time in a day

PullRequestReviewEvent

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

and all integer writes delete any provenance information at the target memory location.

Depending on what exactly you mean by this, this invalidates dead store elimination:

unsafe fn foo(x: *mut usize) {
  let val = *x;
  *x = val; // deletes provenance in target location
}

If we remove the store, that means the transformed program has more provenance information, which can introduce UB into a previously UB-free program.

All integer operations ignore provenance,

What does it mean to "ignore" provenance? If you have two integers a and b with provenance A and B respectively (they might have provenance because they got transmuted from a reference), what is the provenance of a+b? If the answer is "none", note that this proposal also invalidates turning x+0 into x, because the former has no provenance but the latter inherits x's provenance.

mahkoh

comment created time in a day

pull request commentrust-lang/miri

Test std backtrace type

Thanks! @bors r+

What happens when isolation is disabled?

Aaron1011

comment created time in a day

pull request commentrust-lang/miri

rustup; the bad compile times for the float test are fixed

@bors r+

RalfJung

comment created time in 2 days

PR opened rust-lang/miri

rustup; the bad compile times for the float test are fixed
+1 -3

0 comment

2 changed files

pr created time in 2 days

create barnchRalfJung/miri

branch : rustup

created branch time in 2 days

Pull request review commentrust-lang/rust

Note that `BasicBlock` is just a pointer

 rustc_index::newtype_index! {     /// are edges that go from a multi-successor node to a multi-predecessor node. This pass is     /// needed because some analyses require that there are no critical edges in the CFG.     ///+    /// Note that this type is just an index into [`Body.basic_blocks`](Body::basic_blocks);

I don't really understand the question?

camelid

comment created time in 2 days

PullRequestReviewEvent

pull request commentrust-lang/rust

Note that `BasicBlock` is just a pointer

@bors r+ rollup

camelid

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

The specification is a bi-directional contract, by your own admission. The reasoning I can make about surrounding code which can perform an arbitrary action and the reasoning compilers can make are inherently connected (only extended by what I document as undefined behaviour, which is still undefined, not "can lead to undefined behaviour in safe code").

The contract between unsafe code and the compiler, and between unsafe code and the surrounding safe code, is not the same contract though, as I have spelled out a while ago and pointed out many times in this thread.

I get the feeling this is leading nowhere, as we cannot even agree on the most basic principles of how to make the Rust type system precise. Without that shared ground, there is little point in discussing specifics I think.

I would also add that you can make assumptions about the result of undefined behaviour, because a valid result of undefined behaviour is to assign some meaning to it (C++ compilers do it all the time, for example, they allow type-punning through unions, even though the strict rules of C++ make that undefined). If I know what my environment is, I can manipulate my code to cause undefined behaviour in a way that an extension actually gives meaning to, and this is done all the time in low-level software code, including compiler support libraries (I'm pretty sure libcore has some formal UB that rustc has assigned meaning to for that specific reason).

No, no you cannot. That is exactly what UB is about. It does not matter what the hardware does and there is no amount of assumptions that can salvage code with UB, short of auditing the generated assembly code (at which point you might just write assembly yourself).

RalfJung

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

If its not valid for the user to rely on the fact the optimization does not occur, then it shouldn't also be valid to rely on the fact the optimization does occur, or that performing some operation would have the result of changing the variant, even if it does occur.

Agreed.

However, where would that be left? Unspecified behaviour? Interesting behaviour as the result of a particular choice for unspecified behaviour?

I like "interesting behavior", standards should have that. :rofl: More seriously, I think this is just like code that relies on struct field layout -- i.e., unspecified behavior, or more precisely, code relying on unspecified parts of the semantics.

This is exactly what undefined behaviour is, becuse it is definately not defined.

No, not really. With unspecified behavior, if your assumption happens to be right, you actually get guaranteed correct compilation. This is probably an inconsequential difference for programmers, but when thinking about the fine details of these semantics in a formal way, it makes a big difference.

It would arguably be better to specify something as undefined, then have it be undefined because of some wierd language lawyering like this.

I respectfully disagree. We have good formal ways to define Undefined Behavior (as paradoxical as that may sound), and we have good formal ways to define "unspecified aspects of the semantics", and they are formally quite distinct. Once you care about mathematical precision, you thus need to tease these things apart.

The way to define unspecified aspects of the semantics is to parameterize the spec by these aspects. IOW, to actually execute a Rust program, you must make concrete choices for the layout of all structs, enums and unions (and more). These choices are somewhat constrained (e.g. for repr(C) the layout is fully defined, but for repr(Rust) there are very few constraints). For your program to be "well-behaved" Rust code, it must run correctly (without UB) for all possible choices of parameters.

This is very unlike UB, which is typically formalized by some error condition in the abstract machine that is used to specify the possible behaviors of a Rust program.

There definately is code that relies on a reference to the inner field not being able to affect the outer option. I presume that my Option<T>s never become None from out from under me, and when I'm already in unsafe code, I will do manual optimizations that rely on that being the case

This thread is not about assumptions that unsafe code authors can make about unknown safe code. This thread is about assumptions that the compiler can make about arbitrary (safe or unsafe) code.

Generally speaking, right now if you pass a &mut T to a (potentially arbitrary) unsafe function, you got the &mut T from out of an option, and T either has a niche, or is a dependant type that may have a non-hidden niche, you can't assume the reference is still safe to use.

(Again, these are not "dependent types", which means "types that depend on values". We call functions where we do not know the concrete T generic functions in Rust.)

You are conflating "reasoning the unsafe programmer can make about its environment" with "reasoning the compiler can make about unknown code". Unsafe code that calls unknown safe code can certainly rely on that code not "messing" with the niche. This is part of soundness. But achieving the same for the compiler is a lot harder, and much less necessary.

RalfJung

comment created time in 2 days

pull request commentrust-lang/rfcs

Safer Transmute

Maybe another example then:

Good idea, thank you!

Do you agree that b combined with a v1.1 is unsound?

Yes.

Where does the unsoundness originate? In a which has no unsafe code or in b which was written against a v1.0?

This depends on the documentation of a. (Safety is a matter of intent, in particular when unsafe code depends on third-party code.) Does a document that GreaterThan0::val only ever returns things greater than 0? If yes, then its version 1.1 is to blame here. This is similar to the situation where some unsafe code uses a HashMap and relies on that type actually implementing a correct map: even if the HashMap is entirely safe code, a bug in safe code relied on by other unsafe code can lead to UB.

If a documents nothing of the sort, then b is incorrect to rely on an undocumented guarantee.

jswrenn

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

This seems rather strange since the safe-transmute RFC will most likely make transmuting *mut T to usize (and hence &*mut T to &usize) safe. At least I would be very surprised if it didn't.

Indeed, it is rather unsatisfying. But the other alternatives on the table are arguably worse.

The safe-transmute RFC should probably hold back on explicitly blessing such transmutes...

mahkoh

comment created time in 2 days

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

For -> or apply in, or even specialize, do you have some particular semantics in mind? How would you interpret the dependent occurrences of c after the introduction pattern has been applied?

I don't think I understand the question... I imagine that there is a way in Coq to say "run this intro pattern on that term", is that not the case? Probably there are many places where Coq accepts intro patterns that I am not even aware of. For example, where is there a pattern in apply in?

On the other side, would you accept induction c as [=] and if yes with which meaning?

I mean, that is certainly weird, but is it a problem? It would be equivalent to something like induction c as X; [injection X as [=] ].

Also, what kind of destruction would do destruct c when no pattern is given?

That is really the odd one out, isn't it? destruct without a pattern? FWIW in our developments we tell people to not do this, as it leads to unpredictable variable names.

RalfJung

comment created time in 2 days

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

All of these pat could support arbitrary intro patterns without technical trouble, right? So what is the issue?

RalfJung

comment created time in 2 days

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

It is so intuitive to think at destruct as a general destruction tactic that it is worth overloading the basic destruct c as pat form even though it can only have an "inductive-type-destruction" meaning in its more general forms?

Which more general forms are you referring to here?

RalfJung

comment created time in 2 days

push eventmit-pdos/perennial

Ralf Jung

commit sha 61574753cd684cc638b65e0789e0a52cc1c48b65

fix off-by-one in async_update_map spec

view details

Ralf Jung

commit sha 9cd7d3b5a0466275c19a4b7510cef268ed2768eb

finish async proof

view details

push time in 2 days

pull request commentrust-lang/rfcs

Safer Transmute

Channels are sound because it was decided that implementing Send is unsafe. Even though purely safe code cannot do anything with a safe-to-implement Send.

Having both channels and Send be safe is clearly not an option, then we have UB from safe code. You said above that "we could decide that neither should be an unsafe operation"; so this seems like a different situation to me?

jswrenn

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

Even in rust, we have the same thing. A &mut T to the first field of a struct Foo(T,U,V) cannot possibly alter the second or third fields, even though the relative order is not specified. My reasoning depends on this similarly extending to enums

Exactly. And I am saying that is not correct reasoning. The enum discriminant is not a field, it can be arbitrarily encoded in the invalid values of all the fields of the enum, in ways that need not be specified by the compiler.

RalfJung

comment created time in 2 days

issue commentrust-lang/rust

repr(transparent) on generic type skips "exactly one non-zero-sized field" check

I do not see a reason for this restriction. Maybe simply replace "a single" by "at most one" and remove the check.

Agreed. Cc @rust-lang/wg-unsafe-code-guidelines

mahkoh

comment created time in 2 days

issue commentrust-lang/rust

Bors did not report a merge conflict

Also see https://github.com/rust-lang/homu/issues/88.

TimDiekmann

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

I am very doubtful that these strip_provenance operations will not be huge optimization barriers everywhere. At this point everything we can say about this semantics is highly speculative.

Also this makes "the content of a local variable" something special, but it really is not -- y in your foo is also just a name for some address in memory where the value of y is stored. (This is witnessed by the fact that you can take a pointer to y.) But of course we want y to remember its provenance. So you end up with two kinds of memory, some that have provenance and some that do not... and the memory that has provenance (where local variables are stored) still has all the same problems as before.

mahkoh

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

Validity of references: Memory-related properties

It appears that your argument is that compilers should have everything they need given the stacked borrows aliasing model, but if the reference isn't even known to be valid how does it help to know that no one else can see the data (except when they can, because UnsafeCell makes everything harder)? It's too bad that rust never took on the responsibility of doing what LLVM does, because the model is apparently quite a departure from the C like aliasing model, and I would like to see some evidence that most/all of LLVM's optimizations (or more abstractly, the set of general optimizations that have been identified as worthwhile in a compiler) are applicable without dereferenceable and with SB. (This is obviously difficult evidence to procure, but until then I think it is good to hear a compiler dev that says "this causes big problems for me" as a proxy for such evidence.)

It is hard to give this evidence that we can do what LLVM does when LLVM's spec has gaps in the areas we are talking about. ;) In other words, not even LLVM can do what LLVM does, but they do it anyway and live with the resulting miscompilations.

Certainly having feedback about how hard it is to compile Rust to some other IR is useful. Note that we have two backends, and I don't recall @bjorn3 reporting any issues with Rust semantics for the cranelift backend. So we have two backends, both designed for different langauges (C/C++ and wasm, respectively), and as far as we know we can efficiently compile to both of them -- I'd say that is strong evidence that our semantics can be compiled well to a wide range of IRs!

I don't think having to track for each type or each local variable whether its validity can be assumed is too much to ask for an IR. If this would make some style of IR entirely impossible or put a huge burden on the IR author, that would be a different matter, but this is not the case -- this is a slight complication, and I think a that is a fair price to pay for obtaining memory-independence of validity.

So the concern I raised was over optimizations in a generic context, where a dependant type may be a pointer. In the IR I use, pointers are incredibly special, and inventing them out of a bunch of bytes that may not actually have been a pointer is not a thing I want to allow. This may not be a total concern, and I am looking into alternative solutions.

Unfortunately Rust is low-level enough that people can do crazy things... like, say, take a pointer, sign it, send that to another machine, which sends the pointer back to us, we check the signature and then use that pointer. The bytes we got back to us certainly "have never been a pointer" in any reasonable sense of the words (they are just some bytes we got from the network), and yet still it should be possible to use them as a pointer.

However, I fail to see what this has to do with references pointing to valid data. if you want to discuss restricting integer-pointer casts, please open a new thread with a concrete question or problem statement. :)

RalfJung

comment created time in 2 days

issue commentcoq/coq

"destruct" cannot use injection patterns top-level (but it can nested)

The way I think at it, there is no restriction or arbitrary limitation since destruct is for inductive-type "destruction".

On a purely technical level, the implementation of destruct specifically checks for this condition before evaluating the pattern -- and it does not do this check because somehow the pattern would not work, or anything like that. If we removed the check, everything would still work.

In that sense, the limitation feels rather arbitrary.

RalfJung

comment created time in 2 days

issue commentrust-lang/unsafe-code-guidelines

What about: invalid not-used-again values? Is "validity on typed copy" enough?

Writes have to be correct for the type of the reference being used for the write, not the memory location. If you want to write whatever you like, you cast the reference and write at a different type. This part is no different from the current model.

Ah I see, so writes are "allowed to change the type of memory", so to speak.

(also why is that so scary? you haven't elaborated other than to point out that some folks moved away from typed memory in CompCert, which seems like a rhetorical technique rather than an argument)

Not sure if I'd call it "scary", but my dislike for typed memory stems mostly from experience with strict aliasing in C++: it is a huge pain for programmers (see all the projects using -fno-strict-aliasing and the many more projects that should set this flag) and gives very weak optimizations compared to Stacked Borrows. So I think we just don't need it. Also I think there yet has to be a proposal that integrates this well with a language that allows byte-wise memory access and mutation.

Your proposal attempts this, but I think it does not work because Stacked Borrows is per-location, which validity invariants are not (see the discussion about multiple stacks with the same tag).

A "predicate on values" is just u8 -> bool, which is plenty decidable and countable, being isomorphic to [bool; 256]. Probably you wouldn't want that in miri though, as it's pretty big; an enumeration of validity properties that have been defined (aka types) is probably much smaller.

Ah, I thought by "value" you meant something more like this enum. Also "predicate" usually means X -> Prop, not X -> bool, and Prop certainly does not have decidable equality. But indeed if you interpret predicates like that, they remain countable, finite even.

I agree; if references aren't necessarily valid in the first place then there is no point reasserting validity.

Ah, but I think there is -- note that in my example in the OP, the x that is "invalidated in a sneaky way" is not a reference but a local variable!

An alternative approach uses code insertion in the model, i.e. drop impls for mut borrows. You are already doing this with retag so it shouldn't be too much different, and then the drop impl can carry the appropriate type to write at. This approach has its own problems though (where does the code go?) but it seems like this is work that is already done for real drop impls so perhaps it's a solved problem.

The problem is that references are not drop, and this is exploited by the borrow checker. There is no good way to find out, in code, where a lifetime ends -- and with Polonius, that question might not even be well-defined any more.

So I am pretty wary of any proposal that relies on performing some action in the semantics when a lifetime ends. Stacked Borrows was specifically designed to not do that, to avoid these problems.

This is a good point. How do multiple unique borrows with the same tag arise?

When you create an &mut i32, each of the 4 location separately tracks the tag of that reference. Now there are 4 stacks with that tag in them, and removing the tag from these stacks can happen independently and in any order. Only if the last of them is removed would we want to check the invariant again. That seems very non-local compared to the rest of Stacked Borrows.

@chorman0773

As you mentioned, UnsafeCell hides niches (though I would elect not to disable them, I simply tag UnsafeCell in a way that precludes it from being treated as a niche-optimization candidate, the invariants would still be applied),

The invariants certainly still apply. Hiding the niches affects optimizations, it does not affect UB.

Niche optimization of enums depends on the validity invariant in more ways than one. Absent this UB, certain types of Niche Optimizations would, as mentioned by @digma0, alter the behaviour of certain code. So in effect, not having this UB could make the non-mandatory optimizations violate the as-if rule, assuming rust has the as-if rule. According to a strict interpretation of rust's rules (ignoring UCG for this exposition), Some(b), where b is a bool cannot possibly produce None. That is, Some(b).is_some() is a tautology in rust. Using the same reasoning, a reasonable conclusion is that a write to a &mut bool cannot alter the variant. From that conclusion, if it was possible to write in a niche, then niche optimizing Option<bool> is an invalid transformation, because it doesn't satisfy the as-if rule. The only way that it could then be possible to perform the niche optimization is if the as-if rule did not apply, which requires that the program in question have undefined behaviour (This is dependant on rust actually prohibiting this, but I can see nothing in any of the limited documentation rust has that would make the transformation sound, IE. semantically mapping to the untransformed). Note that this is not an authorative interpretation, it's simply my reasonable interpretation, based on the little "normative" information rust has.

This is a good point. I also used to motivate niche optimizations based on these invariants. But I am not sure any more that this is the right approach. Nowadays I see this to be more like struct layout: if you write unsafe code that assumes struct fields are laid out in a particular order, your code is wrong as that order might change in future compiler versions. But your code is not UB, as there is no UB clause that your code violates. We justify struct field reordering not by saying "code that relies on this is UB", we justify it by saying "struct field order is explicitly documented as unspecified, and this may change any time".

So, likewise, we can justify enum layout optimizations by saying "enum layout is explicitly documented as unspecified, and if you assume that the bool in an Option<bool> is in a separate memroy location, you are relying on unspecified details that may change any time, and hence your code is wrong".

RalfJung

comment created time in 2 days

Pull request review commentcoq/coq

Suggesting to use injection when an injection pattern is given to destruct (wish #13205)

 GRAMMAR EXTEND Gram   ;   as_or_and_ipat:     [ [ "as"; ipat = or_and_intropattern_loc -> { Some ipat }+      | "as"; ipat = equality_intropattern ->+        { match ipat with+          | IntroRewrite _ -> user_err Pp.(str "Disjunctive/conjunctive pattern expected")+          | IntroInjection _ -> user_err Pp.(str "Found an injection pattern while a disjunctive/conjunctive pattern was expected; use tactic injection instead.")

So, to make a concrete proposal what about:

use "injection ... as <pattern>" instead.

herbelin

comment created time in 2 days

PullRequestReviewEvent

issue commentrust-lang/unsafe-code-guidelines

Representation of and operations on pointers and usize

I might be missing all the implications of this, but I would like to say that transmutes also strip provenance

That is the third option then. And yes I think you are missing the implications. ;)

nd at least in the simple case it seems like casting a &mut T to a *mut T can just have the same effect as an as-cast

Do you mean "transmuting a &mut T to a *mut T"?

FWIW, most of this discussion is already carried out in this paper that I referenced above. Ctrl-F "Type Punning". Since you seem interested in this topic, I think it would be a good idea to do some background reading to learn about the options people already thought about and why they do not work. :)

As long as provenance is stripped eagerly, there should be no problems justifying this, the value being written has no provenance but the original value didn't have any either.

I don't know what "eager provenance stripping" is... are you saying casting a reference should affect the memory it points to? That has no chance at all to work, it means casts have side-effects so they cannot be reordered any more. They also cannot be removed any more even if their result is unused as that would remove their side-effects.

What are the arguments for provenance existing in memory?

:rofl:

When you have a value x, store it to memory, and then load it back again, I hope we agree that you should get the same value. If you don't do this you just killed store forwarding which is a tremendously important optimization.

mahkoh

comment created time in 2 days

pull request commentrust-lang/rfcs

Safer Transmute

Or we could decide that neither should be an unsafe operation.

FWIW, my comment only applied to the case where "neither is unsafe" does not work because it means UB can be caused. So, situations like leakpocalypse. If "neither is unsafe" is a reasonable option, then my second paragraph doesn't apply.

You keep saying that we need to "decide" if something is unsafe first before we know if things are sound, which seems very circular to me -- usually it works the other way around! I do not see a "leakpocalypse" situation here, so I still think that there is no decision to be made, just a proper analysis of what purely safe code can do with a safe Here!.

But as I said before, I think the main issue here is around the fact that "unsafe types" just are not a thing. I don't know what that term should mean or how to define it precisely (like, in a formal mathematical model of Rust). Relying on the unnamability of types rather than their unconstructibility has never been done before to my knowledge and IMO should be thoroughly explored on its own (i.e., not intertwined with a large and complex API surface) before becoming an accepted reasoning principle in Rust.


@withoutboats thanks a lot for writing this. I largely agree -- this API witnesses that an impressive amount of work was done to understand the problem space, but it feels like the API was then designed to be able to cover every last corner of that space rather than to be simple and easy to learn while still being useful for the majority of cases. We need all that expertise for what the problem space looks like to evaluate if a simple API is good enough, but we do not necessarily need all that expertise reflected directly in the API surface. At least I hope that is the case.

jswrenn

comment created time in 2 days

pull request commentrust-lang/const-eval

RFC for const-UB

I'm not trying to say that. I'm trying to say that it doesn't matter (to me? I was under the impression that it didn't matter to anyone that much, but apparently I was very wrong). We're currently preventing things that the CTFE engine can't handle, and that's enough. The RFC and the T-lang meeting both discuss(ed) how it's too expensive to do a full miri during CTFE and even then there'd still be some very rare and hard UB cases left. The alternative that C++ chose is to just prevent all kinds of operations, which, if we did the same, would severely limit what we can do at const eval time. I don't see us gaining many more improvements to what we have now (like heap allocations, string processing, ...) without unsafe code. I don't see limiting ourselves in the C++ way as a reasonable alternative. I don't even know if the C++ way was chosen out of safety reasons or just to not put a full VM requirement onto compiler developers. If I'd had to put my money anywhere it would be the latter. I also don't know if C++ will keep this scheme or become more liberal in the future.

I agree that once CTFE supports (almost) all of Rust, requiring it to detect all UB is asking too much, and indeed that is not the precedent C++ is setting. This would require "full Miri" and I'd be fine with not doing that.

But at the design meeting, I didn't know about what C++ does, and there are more options between "full Miri" and what this RFC proposes, options that were not considered at the design meeting. Specifically, I think it would be reasonable to expect that for the fragment of operations that C++ supports, Rust is also able to detect UB during CTFE. So basically, if a CTFE evaluation does not use raw pointers, union, or transmute, then we should be able to reliably detect all UB. I think we should at least seriously attempt to actually guarantee this. This is not "run full Miri during CTFE", all it really requires is "run CTFE on unoptimized MIR". The existing checks we are doing should already suffice for this.

In other words, before rejecting the idea, I think we should figure out how expensive it really is to run CTFE on unoptimized MIR.

We'd be breaking a lot of code out there if we suddenly banned everything we can't prove sound. It's already stable to do unsafe things in static and const initializers. So afaict we're really just talking about const fn, even if the wording is independent of the const context.

I am very confused now. Nowhere did I propose to "ban everything we cannot prove sound", so why are you bringing up that proposal now?

Everything in the RFC is about dynamic checks, not static ones! I thought that was obvious as it was talking about unsafe code, of course we can only check unsafe code for UB dynamically...

As you said, CTFE is "only" missing aliasing and validity checks from miri, so if we want to get very pedantic, we can just say that we catch a certain list of UB.

We don't, though. We run after optimizations. Any UB that optimizations exploit, we cannot detect any more reliably after they run.

"the C++ standard says no UB in constexpr" does not seem sufficient (or any) reason to turn 180 degrees on a topic we've discussed to death and had a T-lang meeting on.

We did not discuss this to death at all I think, this is the first serous discussion about it since the design meeting and before that we didn't talk much about it either. New information arose since the design meeting, which is sufficient justification to re-consider some of the conclusions we made before we knew about that information.

RalfJung

comment created time in 2 days

Pull request review commentcoq/coq

Suggesting to use injection when an injection pattern is given to destruct (wish #13205)

 GRAMMAR EXTEND Gram   ;   as_or_and_ipat:     [ [ "as"; ipat = or_and_intropattern_loc -> { Some ipat }+      | "as"; ipat = equality_intropattern ->+        { match ipat with+          | IntroRewrite _ -> user_err Pp.(str "Disjunctive/conjunctive pattern expected")+          | IntroInjection _ -> user_err Pp.(str "Found an injection pattern while a disjunctive/conjunctive pattern was expected; use tactic injection instead.")

I think it would be useful to at least give an example -- I knew about injection before, but this error message would have helped me as I did not know about injection X as [=H1 H2].

herbelin

comment created time in 2 days

PullRequestReviewEvent

pull request commentrust-lang/rust

Add std::thread::available_concurrency

that seemed to work successfully for both FreeBSD and OpenBSD! Though from the output it seems like it may only have checked core but not std?

It should check core, std, and even test. And the output looks like that for me:

$ cargo miri setup --target x86_64-unknown-freebsd
   Compiling compiler_builtins v0.1.35
    Checking core v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/core)
   Compiling libc v0.2.79
   Compiling cc v1.0.60
   Compiling std v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/std)
   Compiling unwind v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/unwind)
    Checking rustc-std-workspace-core v1.99.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/rustc-std-workspace-core)
    Checking cfg-if v0.1.10
    Checking alloc v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/alloc)
    Checking rustc-demangle v0.1.16
    Checking panic_abort v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/panic_abort)
    Checking rustc-std-workspace-alloc v1.99.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/rustc-std-workspace-alloc)
    Checking panic_unwind v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/panic_unwind)
    Checking hashbrown v0.9.0
    Finished release [optimized] target(s) in 25.33s
    Checking rustc-std-workspace-std v1.99.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/rustc-std-workspace-std)
    Checking proc_macro v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/proc_macro)
    Checking term v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/term)
    Checking unicode-width v0.1.8
    Checking getopts v0.2.21
    Checking test v0.0.0 (/home/r/.rustup/toolchains/miri/lib/rustlib/src/rust/library/test)
    Finished release [optimized] target(s) in 1.79s
yoshuawuyts

comment created time in 2 days

more