Generate a compilation error *only* when certain function is called

Hello, everybody.

Is there a way to make the compiler present a compilation error with a custom message that should happen only when the user calls a certain function?

I'm aware of the existence of compile_error, but it seems that its behavior is to bail as soon as the compiler has to "emit" it, and I can't figure out how to do it only when the function gets called.

What I want is for the user to only be able to call certain functions if they had previously marked them using a macro, so the "default" behavior is to generate a compiler error, with the "marked" behavior avoiding that.

Thank you.

How do you mark the functions? If it's with a macro, why even emit the function at all?

As far as I know this is not possible, as the user could for example store a function pointer to the maked function without necessarily calling it :

fn do_not_call() {
    panic!("called !")
}

fn main() {
    let call = do_not_call; // could be a lot more obfuscated 
    if rand::thread_rng().gen() {
        call();
    } else {
        println!("ok");
    }
}

This means that the compiler would also have to throw an error if the user tries to reference the function. In which case it would be easier to just not include the function in the code.

Can you describe why you need this compiler feature? There might be a better way to solve your problem.

I'm not sure this helps, but if the function can be a method, and the struct it exists on are parameterized, you can impl the method only for certain parameters of the struct.

Kind of like this: Implement non-failing builders for Request, Response and Uri. by kaj · Pull Request #483 · hyperium/http · GitHub

If we were talking about a struct, the way you would normally make sure it was annotated with an attribute is by implementing a trait as part of the attribute (e.g. #[derive(MyMarkerTrait)]). Then wherever it is used, you add a T: MyMarkerTrait bound.

It's a bit harder for your application because function items can't be named directly or used as generic parameters, but another general technique for enforcing things at compile time is to use a "tag" and create methods which only exist for a tagged type.

use std::marker::PhantomData;

struct Foo;

struct Tagged<T> {
  _tag: PhantomData<T>,
}

impl<T> Tagged<T> {
  fn new() -> Self { Tagged { _tag: PhantomData } }
}

impl Tagged<Foo> {
  fn bar(&self) {
    println!("Inside bar()");
  }
}

You might also be able to use the linker force something to be used.

As an example, the no-panic crate works by setting things up so that the optimiser must prove some code doesn't panic (i.e. it can successfully optimise out all unwinding code), otherwise it'll try to call an extern "C" function that doesn't exist and cause a linker error.

1 Like