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

Handle return type properly #6438

Closed
wants to merge 1 commit into from

Conversation

richardpringle
Copy link
Sponsor

@richardpringle richardpringle commented Mar 28, 2024

Motivation

Closes #6306

Solution

Explicitly state the return type of the last statement such that the compiler gives the proper error if the types do not match.

I'm looking for some initial feedback. As I'm writing this description, I'm realizing that I could probably wrap the entire body instead of just the last statement.

The error now looks like this:

error[E0308]: mismatched types
 --> src/main.rs:2:20
  |
2 | async fn main() -> Result<(), ()> {
  |          ----      ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()`
  |          |
  |          implicitly returns `()` as its body has no tail or `return` expression
3 |     Ok(())?;
4 |     Ok(());
  |           - help: remove this semicolon to return this value
  |
  = note:   expected enum `Result<(), ()>`
          found unit type `()`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `a-thing` (bin "a-thing") due to 1 previous error

@richardpringle
Copy link
Sponsor Author

richardpringle commented Mar 28, 2024

🤔 can already see it doesn't work for !, any ideas?
nvm

syn::ReturnType::Type(_, ret_type) => ret_type,
};

enum ContainsLoopVisitor {
Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will have to rename this

Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using this enum to find a possible never type, but it might be tough to make this exhaustive in a forward-compatible way. There's panic!, std::process::exit, loop, and core::panicking::panic_explicit() that I know of, but there may be others.

That doesn't include any user-defined functions that return !.

I was getting compiler warnings if I output something like:

let main_body: Result<(), Error> = loop {
    some_fn().await?;
 };

(because the type is obviously wrong)

So I'm not sure that this approach really makes sense...

impl syn::visit::Visit<'_> for ContainsLoopVisitor {
fn visit_expr_loop(&mut self, _: &syn::ExprLoop) {
*self = ContainsLoopVisitor::Found;
}
Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely super wasteful to parse the entire syntax tree until hitting a loop.
Visit has 182 methods and I could add skip! to all of them if *self == Found but that's a lof of nothing code just to fix an error message

// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => &unit_type,
syn::ReturnType::Type(_, ret_type) if ret_type == &never_type => &unit_type,
Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I can actually remove this branch

@richardpringle
Copy link
Sponsor Author

I can fix the let...else after someone take a look at this (CI / minrust failure)

@mox692 mox692 added the A-tokio-macros Area: The tokio-macros crate label Mar 29, 2024
@taiki-e
Copy link
Member

taiki-e commented Mar 29, 2024

Thanks for the PR. This seems to be an approach I said in #3039 (comment)? If so you have to handle at least never type and impl traits. See also #5619 (comment).

NotFound,
}

impl syn::visit::Visit<'_> for ContainsLoopVisitor {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this need to search the input.stmts? Wouldn't it work by just checking the return value?

Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you say the "return value," do you mean the last statement? I guess any statement that contained a loop that doesn't exit would have to be the last statement otherwise any statements that follow would be dead code.

So that would be an improvement, but that doesn't change the fact that using visit could potentially be expensive (with very little gained).

// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => &unit_type,
syn::ReturnType::Type(_, ret_type) if ret_type == &never_type => &unit_type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use matches!(&**ret_type, syn::Type::Never(..)) instead of ==.

@@ -24,7 +24,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.60"
quote = "1"
syn = { version = "2.0", features = ["full"] }
syn = { version = "2.0", features = ["full", "extra-traits", "visit"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both extra-traits and visit are not very good from a view of compile-time...

Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, yup.

Copy link
Sponsor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is doable without these though? I'm asking more about visit, there are workarounds for the PartialEq implementation that I'm using from extra-traits.

@Darksonn
Copy link
Contributor

As discussed on #6306 (comment), I will close this since the path forward is unclear. Still, thank you for taking the time to submit a PR.

@Darksonn Darksonn closed this Apr 11, 2024
@richardpringle
Copy link
Sponsor Author

Hey, thanks for taking the time to review @Darksonn!

All good, I'm sure someone will figure it out at some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tokio-macros Area: The tokio-macros crate
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Erroneous compiler error message for extra semicolon with try operator
4 participants