Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why does dead code have to be valid? #117048

Closed
Vzz1c opened this issue Oct 22, 2023 · 5 comments
Closed

Why does dead code have to be valid? #117048

Vzz1c opened this issue Oct 22, 2023 · 5 comments
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues.

Comments

@Vzz1c
Copy link

Vzz1c commented Oct 22, 2023

use colored::*;
macro_rules! t {
    ($item:expr) => {{
        fn type_of<T>(_: T) -> &'static str {
            std::any::type_name::<T>()
        }
        type_of($item)
    }};
}
fn main() {
    let mut temp_vec: Vec<colored::ColoredString> = Vec::new();
    let test =12_i32;
    if t!(&(test))=="&&str" || t!(&(test))=="&alloc::string::String" || t!(&(test))=="&colored::ColoredString"{
        temp_vec.push(test.white().clear());
    }else if t!(&(test))=="&i32"{
        temp_vec.push(test.to_string().white().clear());
    }
}

I expected to see this happen: It's intuitive. It should compile.

Instead, this happened: temp_vec.push($item.white().clear());
In this code, the compiler reports that there is no white() method.

But here, as long as the if condition is met, the call will succeed without error.

I think the cause of this is similar to the following code

let mystr="test";
if "1"=="2"{
    mystr.foo();
}else{
    println!("no call foo")
}

I don't think anyone in practice would write such code, here if the condition is successful or not, can not call the foo() method, all here reported an error, I can understand. But in the example I defined through the macro, as long as the if condition is met, it will be called successfully. Why does it not compile here?

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Oct 22, 2023
@Vzz1c Vzz1c changed the title Related errors in macro definitions The if-else problem Oct 22, 2023
@saethlin saethlin changed the title The if-else problem Why does dead code have to be valid? Oct 22, 2023
@asquared31415
Copy link
Contributor

It's not possible for the compiler to prove that arbitrary code is dead. There are warnings for some cases of dead code that are easy to detect, but because the compiler cannot perfectly determine if code is dead, it requires all code to be valid. Additionally, dead code can have effects, such as code in an if false causing type inference to be different.

@asquared31415
Copy link
Contributor

any::type_name is especially bad for this case, the name of a type doesn't necessarily have all the information required to construct the type, for example lifetime information.

@workingjubilee workingjubilee removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Oct 23, 2023
@fmease fmease added the C-discussion Category: Discussion or questions that doesn't represent real issues. label Oct 23, 2023
@workingjubilee
Copy link
Member

@Vzz1c When a program includes a call like .white(), it does not dispatch a function call to an in-memory table of functions somewhere, to get resolved at runtime. This is instead a demand that the compiler try to find that the type in question implements a function named white, and that the expression in question is valid for that type. Depending on what order things get looked up in, it will find such a function is available in-scope via the trait Colorize, creating a trait obligation for the compiler to try to fulfill. It then attempts to determine if the type implements Colorize or if, after using auto-deref, one of those types implements Colorize. If it cannot find such an implementation, it cannot proceed to compile the code. And simply "emitting nothing and removing it as dead code" isn't an option, for the reasons already mentioned and also the following:

In the specific case of using the type names, the compiler does not know that the type names of two types cannot resolve to the same string value when pretty-printed by std::any::type_name! There is no such requirement in the language. In fact, actual Rust programs do have different types that could have the same string value returned from std::any::type_name, for instance, because they are actually from the "same" crate but with different versions. So a programmer, and certainly not the compiler, cannot reason from type names to trait impls (or even lack thereof).

Because of that, there is less than one might imagine stopping the compiler from compiling-in an "impossible" condition check, failing to remove it because the condition is not clearly impossible, and at runtime, evaluating to something that causes a jump to that "undefined" code segment. This does happen in C++ compilers, where legal optimizations remove some "dead code", resulting in falling-through to a random function.

Because of this, it is much, much easier to simply reject all programs that try this than to accept any of them, because all of them are ill-formed. Rust already has too many problems with accidentally compiling correct programs in invalid ways for it to be likely anyone wants to try to allow this.

This is working-as-intended.

@Vzz1c Vzz1c closed this as completed Oct 23, 2023
@DoubleHyphen
Copy link

I'm fairly certain this is an XY problem, anyway.

Best as I can tell, your fundamental desire is not to ensure dead code doesn't have to be valid. Your fundamental desire is to achieve type introspection in Rust. The question is, do you want to achieve that statically (ie at compile-time) or dynamically (ie at run-time)?

If it's the latter, I guess you could use std::any::Any and attempt to down-cast to a specific type. If that doesn't work, use your new vocabulary to ask around.

If it's the former… hoo boy, I don't envy you. With introwospection killed stone dead due to drama, I think we're SOL in that regard.

@Vzz1c
Copy link
Author

Vzz1c commented Oct 23, 2023

I started out using downcast_ref, I thought it was too much of a hassle so I tried it that way, I now know it's limiting so I've changed it back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-discussion Category: Discussion or questions that doesn't represent real issues.
Projects
None yet
Development

No branches or pull requests

6 participants