-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Compile-time checked version of unimplemented!()
#1911
Comments
Posted to internals at https://internals.rust-lang.org/t/compile-time-checked-version-of-unimplemented/4837 |
I feel like there might be a misunderstanding of having type Your existing macro_rules! incomplete {
() => {{
#[deny(non_snake_case)]
let _mustBeImplemented = ();
unsafe { ::std::mem::uninitialized() }
}}
} Another idea instead of the |
@cuviper Ah, no, I'm aware of the semantic meaning of unsafe { ::std::mem::uninitialized() } trick, and used |
I realized that has a type-inference problem if used in statement context, macro_rules! incomplete {
($ty:ty) => {{
#[forbid(non_snake_case)]
let _mustBeImplemented = ();
unsafe { ::std::mem::uninitialized::<$ty>() }
}};
() => { incomplete!(_) }
}
fn main(){
let _x: () = incomplete!(); // inferred type
incomplete!(()); // explicit type `()`
} Also, I think |
Oh, yeah, good catch. |
I've also been thinking about whether there's a better way to have the displayed error be more relevant (obviously, having this be a "true" compiler error would be better). I was hoping to use macro_rules! incomplete {
($ty:ty) => {{
if false {
loop{};
#[deny(unreachable_code)]
() // must be implemented
}
unsafe { ::std::mem::uninitialized::<$ty>() }
}};
() => { incomplete!(_) }
}
fn main() {
let _x: () = incomplete!(); // inferred type
incomplete!(()); // explicit type `()`
} namely
though for some reason the warning doesn't seem to be turned into an error (neither does using |
Hmm, the lint controls don't seem to work on statements -- only on items? Here's a way: #[forbid(unused_must_use)]
fn _incomplete() { Err::<(), ()>(()); } But it gets an extra note about where the lint level was set:
Maybe there's a lint to trigger that's default deny/forbid. |
Good enough? fn _incomplete() { '_: loop {} }
Still hacky to abuse lints, of course -- not as good as it could be as a real compiler macro. |
That last one is pretty good. I liked the unreachable code one primarily because the message is very clear, highlighting the |
FWIW, I made a crate: https://github.com/jonhoo/incomplete. |
Actually, come to think of it, maybe all I really want is a lint like #![warn(unimplemented)] or #![deny(unimplemented)] |
I noticed that the |
fn my_fn() -> usize {
unimplemented()!;
0
} produces:
Does this suffice to point out left-over |
@sanmai-NL You mean
|
I believe this is either |
@Centril I don't think that's quite accurate. |
@jonhoo so... the only difference between |
Isn't the goal here |
@burdges No, there are reasonable cases where I want to run my code when it still has @Centril yes, it's perhaps a little niche, but it would be super handy when doing bigger refactoring. If I'm not mistaken, |
cc @oli-obk re. this question |
No, after checking. all const eval happens after borrow checking and Mir computation for that item. other items may be in different states though. On topic of this feature request. this is doable in clippy and feels niche enough to me to not be in the compiler |
@oli-obk If this were in clippy it wouldn't actually be useful, because I don't think most developers regularly run clippy. You do however regularly run the compiler. It's an interesting point that |
(note: the |
It only runs in the end for the containing item. you might error out immediately on that and never see your other errors. I don't see how being in clippy would make this less useful. Rls will show it, and if your workflow uses this kind of check, then your workflow will just contain clippy from now on 😃 That said. This kind of scheme does not fit into queries very well. Any improvements for queries will likely end up reporting these errors earlier again. I do think the grep method is the most reliable one, or doing it in clippy, which doesn't need to uphold the query system As a hack you could create this macro on nightly yourself by using broken inline assembly. |
Rls will only show it if clippy is enabled :) I'm working on a 50k LOC codebase that wasn't originally written with clippy enabled, so it's not actually feasible to have clippy running all the time (at least not yet) because it generates too much noise. You might be right that for most projects a clippy lint is the way to go, but what would the macro then ever consist of? It would have to also be possible to compile outside of clippy? |
I quite like the idea of
which permits seeing reminders for only a fragment of the tree too. |
You can run it all the time and just enable the lints you feel confident about. E.g. leave all lints disabled and just enable the one about You might be right that for most projects a clippy lint is the way to go, but what would the macro then ever consist of? It would just forward to
yes indeed. Without clippy it would just be the same as writing Even easier would be to simply find all |
Could this be implemented by linking to an external function that doesn't exist so the failure is pushed back until an exe is produced? See the panic-never crate |
Personally I would want a stronger thing which likely cannot be implemented as a library macro. Instead of just delaying the check to the end of the compiler pipeline, I would like Possibly This is similar to |
Dead code elimination for whole functions is done as part of the linker. ( |
Yes, I don't expect this will ever be in the language. Still, it would be a cool thing to have. Maybe it could be done as a Clippy lint? It could be a check which traces all function calls starting from the public API and errors out on the calls to the macro. |
That could work, though it would need to look at all roots, including trait object creation and function pointer creation. It would also still fire for trivially unreachable things like |
We all know and love the
unimplemented!()
macro for keeping track of things we haven't yet gotten around to. However, it is only checked at runtime if a certain code path is encountered. This is fine in many cases, but when writing new code from scratch (especially when porting), refactoring large amounts of code, or building comprehensive new features, you often have segments of code that you haven't implemented yet, but you know you'll have to.At least for me, it is common to write a chunk of code, but leaving out some annoying edge cases, or maybe logic (like generating a unique identifier) that I want to deal with alter. I know that I will have to complete that block of code before the program does something useful, but I want to do it later. This can be either because I want to complete the main logical flow of the code first, because I haven't figured out how to do that part yet, or simply because I want to see if the rest of my code compiles correctly.
It would be extremely useful to have a variant of
unimplemented!()
that would generate a compile-time error if present in my code. Something likeincomplete!()
. However, it should have some properties that I believe mean it can't be implemented without some help from the compiler:!
forunimplemented!()
)incomplete!()
.unimplemented!()
does).The closest I've been able to come up with on my own is the following
This will only error out after all compiler passes (2), and since it returns
!
, it is sort of usable in place of any expression (1). However, it does not fit (3), because the infinite loop makes the compiler believe that all code following it is dead. Furthermore, the error it produces is obviously not appropriate for what the macro's intent is.NOTE: originally posted as rust-lang/rust#39930, but moved here following @sanmai-NL's suggestion.
The text was updated successfully, but these errors were encountered: