about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManish Goregaokar <manishsmail@gmail.com>2015-07-18 13:41:44 +0530
committerManish Goregaokar <manishsmail@gmail.com>2015-07-23 12:49:25 +0530
commit74768e9a6ccac85f813e4125c76f9cb52a2ac618 (patch)
treef5755389b80e9b646192ce027af31f0db1fc458f
parent3080df36e27ed74399f9fe22e4796e209abd2b69 (diff)
downloadrust-74768e9a6ccac85f813e4125c76f9cb52a2ac618.tar.gz
rust-74768e9a6ccac85f813e4125c76f9cb52a2ac618.zip
Add long diagnostics for E0139 (type parameters in transmute)
-rw-r--r--src/librustc/diagnostics.rs65
1 files changed, 64 insertions, 1 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index a409ba62f91..02f4cea87ca 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -821,6 +821,70 @@ This error indicates that the compiler found multiple functions with the
 point into a Rust program.
 "##,
 
+// FIXME link this to the relevant turpl chapters for instilling fear of the
+//       transmute gods in the user
+E0139: r##"
+There are various restrictions on transmuting between types in Rust; for example
+types being transmuted must have the same size. To apply all these restrictions,
+the compiler must know the exact types that may be transmuted. When type
+parameters are involved, this cannot always be done.
+
+So, for example, the following is not allowed:
+
+```
+struct Foo<T>(Vec<T>)
+
+fn foo<T>(x: Vec<T>) {
+    // we are transmuting between Vec<T> and Foo<T> here
+    let y: Foo<T> = unsafe { transmute(x) };
+    // do something with y
+}
+```
+
+In this specific case there's a good chance that the transmute is harmless (but
+this is not guaranteed by Rust). However, when alignment and enum optimizations
+come into the picture, it's quite likely that the sizes may or may not match
+with different type parameter substitutions. It's not possible to check this for
+_all_ possible types, so `transmute()` simply only accepts types without any
+unsubstituted type parameters.
+
+If you need this, there's a good chance you're doing something wrong. Keep in
+mind that Rust doesn't guarantee much about the layout of different structs
+(even two structs with identical declarations may have different layouts). If
+there is a solution that avoids the transmute entirely, try it instead.
+
+If it's possible, hand-monomorphize the code by writing the function for each
+possible type substitution. It's possible to use traits to do this cleanly,
+for example:
+
+```
+trait MyTransmutableType {
+    fn transmute(Vec<Self>) -> Foo<Self>
+}
+
+impl MyTransmutableType for u8 {
+    fn transmute(x: Foo<u8>) -> Vec<u8> {
+        transmute(x)
+    }
+}
+impl MyTransmutableType for String {
+    fn transmute(x: Foo<String>) -> Vec<String> {
+        transmute(x)
+    }
+}
+// ... more impls for the types you intend to transmute
+
+fn foo<T: MyTransmutableType>(x: Vec<T>) {
+    let y: Foo<T> = <T as MyTransmutableType>::transmute(x);
+    // do something with y
+}
+```
+
+Each impl will be checked for a size match in the transmute as usual, and since
+there are no unbound type parameters involved, this should compile unless there
+is a size mismatch in one of the impls.
+"##,
+
 E0152: r##"
 Lang items are already implemented in the standard library. Unless you are
 writing a free-standing application (e.g. a kernel), you do not need to provide
@@ -1608,7 +1672,6 @@ register_diagnostics! {
     // E0006 // merged with E0005
 //  E0134,
 //  E0135,
-    E0139,
     E0264, // unknown external lang item
     E0269, // not all control paths return a value
     E0270, // computation may converge in a function marked as diverging