Custom attributes in no-std

How I can create a custom attribute for functions and do something when a function uses it in no-std?
Like procedural macros but in no-std.

You can use proc macros with no_std, can't you? They'll only need to run at compile time anyways, whereas the restrictions for proper no_std crates should only apply at run-time.

1 Like

Are you trying to make the function compile differently in std and no_std cases, applying some kind of transformation for the latter?

I tried to use proc_macro in no-std, but I get:
error[E0463]: can't find crate for proc_macro

My rustc version is rustc 1.63.0-nightly (21e9336fe 2022-06-18).

I'm writting a kernel and I need a custom test framework because I cannot use std (uses heap allocation, threads...). I need to know if a function has the #[should_panic] attribute for changing the comportment in the test_runner.

How are you building your project? Proc macros should be compiled for the host which has libstd and libproc_macro available.

1 Like

Of course, I can't use proc-macro in no-std. I'm only searching a replacement.

As far as I am aware, proc macros and patching the compiler are the only two ways to add custom attributes to Rust.

Because proc macros are an entirely compile-time concept, there’s no sense in writing a no-std proc macro crate: For the compiler to run, all of std needs to be available anyway.

If the code emitted by a proc macro doesn’t require std, then you should be able to invoke the macro from a no-std crate, even though the proc macro crate itself requires std.

How can I implement that? Can you show me an example?

Proc macros can be used just fine in no_std crates. They are compiled for the host and as such should have a full std capable sysroot available when compiling.

But, how can I implement that I tried using proc-macros in no-std but I get can't find crate for proc-macro? What I am doing wrong? Should I enable some flag in Cargo.toml?

Sorry, but I didn't put the proc-macro=true in Cargo.toml.
Now I can use procedural macros but when I try to compile I get error: proc-macro crate types currently cannot export any items other than functions tagged with #[proc_macro], #[proc_macro_derive], or #[proc_macro_attribute].

So, how can I have proc-macros without broke the other part of the crate?

You need two crates:

  • One with proc-macro=true which defines the macro, and can use whatever std features it needs
  • Another, with no-std, that has your main program code and lists the first as a dependency (or maybe a dev-dependency, I'm not sure which)
1 Like

But, in the proc-macro crate I need features from my core crate. For example, I need my own version of the println macro because it writes directly to the vga_buffer. It's a kernel!

When I try to have my core package that depends on a separated crate with proc-macros that depends on my core package, I get error: cyclic package dependency: package asensio-kernel v0.1.0 (/home/juanjo/asensio-kernel) depends on itself.

And that's logical. But how can I fix it? I think to solve this with cargo it's impossible for now.

Does your proc macro need to call the main code at compile time, or does it just need to emit code that refers to the main crate? If you have the latter case, like most proc macros, the proc macro crate doesn't need to declare a dependency on the main one.

If you do need to call some of your main code at compile time, you'll actually need three crates:

  1. Core functionality, written with no-std, that is a dependency of the other two
  2. A proc macro crate, which can use both std and the functions in your core crate
  3. A user interface crate, which depends on both of the above and provides the API designed for downstream programmers

My proc-macro needs to assign a global variable in the main crate.

That presumably happens at runtime, so you just need to emit code like this (or whatever mechanism you're using for mutable globals):

unsafe { ::main_crate_name::GLOBAL_VAR = some_calculation() };

That doesn't require you to link the main crate into the compiler, which is what you're trying to do by listing it as a dependency of the proc macro crate.

If, on the other hand, you wanted to write a proc macro that precalculates some complicated data structure and bakes it into the program, you would need to link the core datastructure library into both the compiler and the main program: The compiler needs to know how to build it in the first place and the program needs to know how to interpret it at runtime. This is the sort of situation that will require 3 different crates.

I get error[E0433]: failed to resolve: could not find asensio_kernel in the list of imported crates.

Have you got any example that works for checking what I am doing wrong?