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

Compile causes warnings about macros #399

Open
kpcyrd opened this issue May 3, 2017 · 9 comments
Open

Compile causes warnings about macros #399

kpcyrd opened this issue May 3, 2017 · 9 comments

Comments

@kpcyrd
Copy link

kpcyrd commented May 3, 2017

Compiling my project results in a bunch of warnings:

warning: code relies on type inference rules which are likely to change
  --> src/main.rs:83:21
   |
83 |       server.get("/", middleware! { |_, res|
   |  _____________________^ starting here...
84 | |         let mut data = HashMap::new();
85 | |         data.insert("name", "user");
86 | |         return res.render("templates/index.html", &data);
87 | |     });
   | |_____^ ...ending here
   |
   = note: #[warn(resolve_trait_on_defaulted_unit)] on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #39216 <https://github.com/rust-lang/rust/issues/39216>
   = note: this error originates in a macro outside of the current crate

warning: unreachable expression
  --> src/main.rs:83:39
   |
83 |     server.get("/", middleware! { |_, res|
   |                                       ^^^
   |
   = note: #[warn(unreachable_code)] on by default

warning: code relies on type inference rules which are likely to change
  --> src/main.rs:89:28
   |
89 |       server.get("/e/:hash", middleware! { |req, res|
   |  ____________________________^ starting here...
90 | |         let hash = req.param("hash").unwrap();
91 | |
92 | |         let mut data = HashMap::new();
93 | |         data.insert("hash", hash);
94 | |         return res.render("templates/view.html", &data);
95 | |     });
   | |_____^ ...ending here
   |
   = note: #[warn(resolve_trait_on_defaulted_unit)] on by default
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #39216 <https://github.com/rust-lang/rust/issues/39216>
   = note: this error originates in a macro outside of the current crate

warning: unreachable expression
  --> src/main.rs:89:48
   |
89 |     server.get("/e/:hash", middleware! { |req, res|
   |                                                ^^^
   |
   = note: #[warn(unreachable_code)] on by default

This happens with router! as well. My code is very similar to the examples on http://nickel.rs, I'm not sure if I'm doing something wrong.

@davidpodhola
Copy link

Same here, related to rust-lang/rust#39216

@jolhoeft
Copy link
Member

This appears to be causing four tests to fail during cargo test.

@jolhoeft
Copy link
Member

This seems to be only a problem when using the middleware macro. Implementing the Middleware trait bypasses this issue.

@jolhoeft
Copy link
Member

The problem arises in the middleware macro (in
https://github.com/nickel-org/nickel.rs/blob/master/src/macros/middleware.rs),
specifically in the _middleware_inner support macro. This snippet of
code from the form_data example:

server.get("/", middleware! { |_, res|
    let mut data = HashMap::new();
    data.insert("title","Contact");

    return res.render("examples/form_data/views/contact.html", &data)
});

expands to (via cargo expand):

server.get("/", {
    // use ::{MiddlewareResult, Responder, Response, Request};
    // #[inline(always)]
    fn restrict<'mw, D, R: Responder<D>>(r: R, res: Response<'mw, D>) -> MiddlewareResult<'mw, D> {
        res.send(r)
    }
    // #[inline(always)]
    fn restrict_closure<F, D>(f: F) -> F
        where F: for<'r, 'mw, 'conn>Fn(&'r mut Request<'mw, 'conn, D>, Response<'mw, D>)-> MiddlewareResult<'mw, D> + Send + Sync {
        f
    }
    restrict_closure(move |_, res| {
        restrict({
            let mut data = HashMap::new();
            data.insert("title", "Contact");
            return res.render("examples/form_data/views/contact.html", &data)
        }, res)
    })
});

The compiler warning is on the call to the restrict method, which is
triggering the resolve_trait_on_defaulted_unit warning. Apparently we
not being explicit enough in types. My initial thought was to
explicitly set the type parameter D to () (the correct value in this
case) in the expanded code, but that failed to fix the warning.

@lawliet89
Copy link

lawliet89 commented Sep 18, 2017

So I was trying to build the form_data example and I kept getting:

warning: unreachable expression
  --> examples/form_data/form_data.rs:8:21
   |
8  |       server.get("/", middleware! { |_, res|
   |  _____________________^
9  | |         let mut data = HashMap::new();
10 | |         data.insert("title","Contact");
11 | |
12 | |         return res.render("examples/form_data/views/contact.html", &data)
13 | |     });
   | |_____^

Expanded, it becomes:

warning: unreachable expression
  --> examples/form_data/form_data.rs:34:17
   |
34 |                 res,
   |                 ^^^
   |
   = note: #[warn(unreachable_code)] on by default

I realised what's probably happening:

  1. The return res.render("examples/form_data/views/contact.html", &data) is causing your expanded macro to never invoke restrict.
  2. Therefore the compiler cannot infer the type of res.
  3. According to Tracking issue for future-incompatibility lint resolve_trait_on_defaulted_unit rust-lang/rust#39216, it used to infer this as () but no longer.

If you change your expanded code to something like:

#[macro_use] extern crate nickel;
use nickel::{Nickel, HttpRouter, FormBody};
use std::collections::HashMap;

fn main() {
    let mut server = Nickel::new();

    server.get("/", {
        use nickel::{MiddlewareResult, Responder, Response, Request};
        #[inline(always)]
        fn restrict<'mw, D, R: Responder<D>>(
            r: R,
            res: Response<'mw, D>,
        ) -> MiddlewareResult<'mw, D> {
            res.send(r)
        }
        #[inline(always)]
        fn restrict_closure<F, D>(f: F) -> F
        where
            F: for<'r, 'mw, 'conn> Fn(&'r mut Request<'mw, 'conn, D>, Response<'mw, D>)
                                   -> MiddlewareResult<'mw, D>
                + Send
                + Sync,
        {
            f
        }
        restrict_closure(move |_, res| {
            restrict::<_, ()>(
                {
                    let mut data = HashMap::new();
                    data.insert("title", "Contact");
                    return res.render("examples/form_data/views/contact.html", &data);
                },
                res,
            )
        })

    });

    server.post("/confirmation", {
        use nickel::{MiddlewareResult, Responder, Response, Request};
        #[inline(always)]
        fn restrict<'mw, D, R: Responder<D>>(
            r: R,
            res: Response<'mw, D>,
        ) -> MiddlewareResult<'mw, D> {
            res.send(r)
        }
        #[inline(always)]
        fn restrict_closure<F, D>(f: F) -> F
        where
            F: for<'r, 'mw, 'conn> Fn(&'r mut Request<'mw, 'conn, D>, Response<'mw, D>)
                                   -> MiddlewareResult<'mw, D>
                + Send
                + Sync,
        {
            f
        }
        restrict_closure(move |req, res| {
            restrict::<_, ()>(
                {
                    let form_data = {
                        match req.form_body() {
                            ::std::result::Result::Ok(val) => val,
                            ::std::result::Result::Err(e) => return Err(From::from((res, e))),
                        }
                    };
                    println!("{:?}", form_data);
                    let mut data = HashMap::new();
                    data.insert("title", "Confirmation");
                    data.insert(
                        "firstname",
                        form_data.get("firstname").unwrap_or("First name?"),
                    );
                    data.insert(
                        "lastname",
                        form_data.get("lastname").unwrap_or("Last name?"),
                    );
                    data.insert("phone", form_data.get("phone").unwrap_or("Phone?"));
                    data.insert("email", form_data.get("email").unwrap_or("Email?"));
                    return res.render("examples/form_data/views/confirmation.html", &data);
                },
                res,
            )
        })
    });


    server.listen("0.0.0.0:8080").unwrap();
}

it builds. The change was to explicitly state the type parameter R of restrict is ().

P.S.: I thought the return line in your macro invocation was quite unidiomatic and very unexpected.

@jolhoeft
Copy link
Member

jolhoeft commented Nov 12, 2017

The unidiomatic return does seem part of the underlying cause. All the places where this bug turns up so far have that pattern. The cases where it doesn't work fine, since the final res sets the type of the return.

Unfortunately adding the type signature to the macro is breaking the use cases without the return.

@jolhoeft
Copy link
Member

I am starting to understand. The middleware macro looks like it takes a closure, but actually is unpacking what looks like one. The block is supposed to return something implementing Responder, which is then passed to the Response. res.render doesn't, but the return statement breaks out of the macro expansion before the type issues manifest, except as the inference failure we are seeing here. This seems to be rather fragile.

I think the middleware macro will need to be reworked in an API breaking way. Currently it is being used in two different ways. One, to emulate a closure that returns aResponder, and the other to emulate a closure that returns a MiddlewareResult.

@jolhoeft
Copy link
Member

I am not seeing a way to fix this without breaking existing code. Since the migration to hyper 0.11.a in #402 is going to change the API anyway, I plan on addressing this as part of the eventual nickel 0.11.

jolhoeft added a commit to jolhoeft/nickel.rs that referenced this issue Nov 19, 2017
Fix the examples that were returning MiddlewareResults in the
middlware! macro. A couple could be changed to return a Responder, but
most needed to be reimplemented as seperate functions.

Updated the middleware! macro documentaion to note the limitations
from issues nickel-org#399 and nickel-org#389.
jolhoeft added a commit to jolhoeft/nickel.rs that referenced this issue Nov 19, 2017
Fix the examples that were returning MiddlewareResults in the
middlware! macro. A couple could be changed to return a Responder, but
most needed to be reimplemented as seperate functions.

Updated the middleware! macro documentaion to note the limitations
from issues nickel-org#399 and nickel-org#389.

Disable ssl testing in travis, which was broken in commit
8d5a7d0. Issue nickel-org#415 to track
our SSL plan.
@jolhoeft
Copy link
Member

PR #414 addresses this issue partially, in that it fixes currently breaking examples and documents the macro's limitations.

jolhoeft added a commit that referenced this issue Nov 20, 2017
fix(examples): Fix examples triggering issue #399
jolhoeft added a commit to jolhoeft/nickel.rs that referenced this issue Nov 23, 2017
Fix the examples that were returning MiddlewareResults in the
middlware! macro. A couple could be changed to return a Responder, but
most needed to be reimplemented as seperate functions.

Updated the middleware! macro documentaion to note the limitations
from issues nickel-org#399 and nickel-org#389.

Disable ssl testing in travis, which was broken in commit
8d5a7d0. Issue nickel-org#415 to track
our SSL plan.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants