Do you have some particular example in mind? The only things I can think of that could make it leak would be if LLVM decides to use the target features on surrounding safe code (e.g. due to inlining or a bug), which AFAIK is not the case.
I also think that safe wrappers around target feature will probably never be perfect, but err on the safe side. For example, a cpuid crate that tells whether a cpu support some feature should return false if it doesn’t know (e.g. because it doesn’t know the CPU, the feature, or it cannot parse the flags, or there was some system error, or…). Anyhow, if there is a problem (e.g. the cpuid crate has a bug), you can always follow the unsafe code to find out where that bug comes from.
Does that make sense? Or do you mean something else by “leak” ?
I don’t think this is considered safe; it is just not considered. If something goes wrong, the behavior is, to the best of my knowledge, completely undefined. If you are lucky you’ll get an LLVM error which is something that should never happen. I consider these flags as a way of talking with LLVM directly, while defining some cfg! macros at the same time.
The most important thing we should document is that they define some cfg! values, which is the only thing that directly affects Rust-The-Language. We should also document that they pass some flags to the LLVM code generation backend, and that users should what are the LLVM preconditions on those flags, what are their semantics, and how they should be used so that they do not generate undefined behavior within LLVM nor Rust.
This answer might be unsatisfying, but I don’t think we can do better. On one hand, these things are implementation defined. More often than not even LLVM doesn’t know what the exact semantics of some of these flags exactly are. On the other hand, these features exist to allow users do this kind of things. Both are in tension.
I would also personally feel that running code with a target feature that your CPU doesn’t support is memory unsafe without more data supporting such a conclusion. […] Are we ready to completely hamstring Rust’s SIMD story based on this theoretical outcome?
Making #[target_feature] require unsafe fn only actually implies that calling a function on a CPU that doesn’t support the features it uses can potentially introduce memory unsafety. I only work on platforms that raise a SIGILL exception when this happens, so in my experience, this is never the case because your process is guaranteed to crash. I don’t know if this is the case everywhere though, but even if the worst thing that can happen is a crash, I don’t think that making them unsafe is that bad (the worst thing that can happen on nullptr dereference is often also a crash). Yet, and this is my opinion, a truly safe wrapper wouldn’t just crash, but rather panic with a nice error message.
It seems to me, however, that you meant to say what you need is proof that if we make #[target_feature] safe it would be possible to use it to create “real” memory unsafety in pure safe Rust code. I agree that we should try to see if this is actually the case, and in particular, which target features currently available in Rust do we actually need to create it.
@burntsushi showed an example above in which the semantics of the program are changed by using target feature. I think that it is possible to adapt his example to create memory unsafety by just encoding pointers as integers and back, and use those to read from memory. With target feature enabled the value of those pointers would change, producing memory unsafety when memory is read. The main issue I have with this example is that it does require writing unsafe code. However, the unsafe code is correct with target feature disabled, and incorrect with it enabled. Maybe somebody can come up with a better example during the RFC process that does not require unsafe.
Another thought.
Replying to your second post, you are somehow implying that making #[target_feature] unsafe somehow makes SIMD in Rust worse. I don’t see how. SIMD intrinsics are unsafe anyways, and a safe SIMD wrapper can provide a safe abstraction over target feature in the same way that it can provide a safe abstraction over these intrinsics.
Since #[target_feature] will need to support multiple, sometimes very different, target features, across possibly different backends in the future, I would be wary of making it safe, at least initially, and then trying to workaround this soundness language bug.
I am with @burntsushi in that we should either make target_feature safe or unsafe, but not make this “feature-combination dependent”. I also think that making it unsafe won’t be that bad in practice, and given that he already found a trivial way to modify the behavior of a program using target feature, I would rather err on the safe side for an initial RFC. Still, we shouldn’t make things unsafe lightly, and I will definetly write this as an issue that we must resolve before stabilization. I hope that during the RFC process some people will try to prove that a safe target_feature is unsound, at least with some of the features available on nightly, and also hope that they will succeed, since this will make it clear that the feature must be unsafe to use. Not because I like unsafe, but because I don’t think that we can ever prove that this is safe to use, and if nobody manages to break it, we really will never know if its sound or unsound to make it safe.