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

Nested function scoped type parameters #3562

Closed
wants to merge 5 commits into from

Conversation

jake-87
Copy link

@jake-87 jake-87 commented Jan 31, 2024

Allow type parameters to be used in nested functions. Removes E0401.

This error was first introduced (at the latest) in 2015. After almost ten years of development and changes in Rust, I believe it's time to reconsider it.
TLDR: Allow the below.

fn foo<T>(x: T) -> T {
    fn bar(y: T) -> T {
        y
    }
    bar(x)
}

Rendered

@jake-87
Copy link
Author

jake-87 commented Jan 31, 2024

Apologies, I do not know how to assign tags. I believe this proposal should at least have T-lang.

@shepmaster shepmaster added the T-lang Relevant to the language team, which will review and decide on the RFC. label Jan 31, 2024
@Jules-Bertholet
Copy link

Would this also allow using type parameters from an outer function inside the inner function's body, even when they do not appear in the inner function's signature? E.g.,

fn outer<T: Default>() {
    fn inner() {
       let _ = T::default();
    }
}

This would mean that the compiler has to inspect inner's body before being able to tell that it's generic. Also, one of the main uses of nested inner functions is for deliberately avoiding separate monomorphizations; this change could make it easier to accidentally introduce genericity when it's not desired.

Also, how does this interact with attributes that require a non-generic function, like #[export_name] and #[no_mangle]?

Finally, should const or static items also be able to use generic parameters from the outer function?

@jake-87
Copy link
Author

jake-87 commented Feb 4, 2024

No, I am not proposing to allow that example, for the exact reasons you've raised.

I'm unsure what you mean by "interacting with #[export_name] and #[no_mangle]" - if there's no generic parameters, you can't use this feature.

With regards to static and const, They already cannot, and I see no reason to change that. In those two examples, it's quite reasonable that they cannot use a generic parameter.

@SkiFire13
Copy link

I'm unsure what you mean by "interacting with #[export_name] and #[no_mangle]" - if there's no generic parameters, you can't use this feature.

I believe they meant something like this:

fn foo<T>(x: T) -> T {
    // This attribute requires `bar` to not be generic, but this then requires determining that doesn't use `T`
    #[no_mangle]
    fn bar(y: i32) -> i32 {
        y
    }
    // This cannot be allowed, but notice how it has the same signature as `bar`!
    #[no_mangle]
    fn baz(y: i32) -> i32 {
        T::some_function(y)
    }
    bar(x)
}

With regards to static and const, They already cannot, and I see no reason to change that. In those two examples, it's quite reasonable that they cannot use a generic parameter.

IMO it would be confusing if functions can use generic parameters declared outside and static/const can't. Also, what about structs, traits and impls?

@jake-87
Copy link
Author

jake-87 commented Feb 5, 2024

I would propose to reject both of your examples, noting the same reasoning that @Jules-Bertholet provided. Whether a function can be #[no_mangle] (or similar) should be determinable from the type signature, which it will still be - simply an occurs check.

Correct me if i'm wrong, but static and const should not be able to as there is no way to obtain a static/const value without arbitrary code execution, rending this useless anyway due to the generic nature of the type. Perhaps the error message could be changed from E0401 to something more informative.

Structs being able to utilize this seems reasonable (although perhaps not productive? may encourage longer functions using more internal structs - happy to take further comment on this one)

However, traits and impls would probably not benefit from this, and further complicating trait resolution seems a fool's errand. I do not want to "half-arse" this feature, but these two seem much more work then they're worth (I've also never seen a local trait)

@teor2345
Copy link
Contributor

teor2345 commented Feb 8, 2024

I would propose to reject both of your examples, noting the same reasoning that @Jules-Bertholet provided.

It might be helpful to add the examples and rationale to the RFC. It could go in the rationale and alternatives section, but I'm not sure if that's the best place.

@jake-87
Copy link
Author

jake-87 commented Feb 8, 2024

I will, yes - I've ran into some busyness, but I'll do it when I find the time.

@jake-87 jake-87 closed this Apr 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants