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

Update non-local-defns regarding parameterized traits and types #3581

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 54 additions & 17 deletions text/3373-avoid-nonlocal-definitions-in-fns.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
- Feature Name: N/A
- Start Date: 2022-01-19
- RFC PR: [rust-lang/rfcs#3373](https://github.com/rust-lang/rfcs/pull/3373)
- RFC PR #2: [rust-lang/rfcs#3581](https://github.com/rust-lang/rfcs/pull/3581)
- Tracking Issue: [rust-lang/rust#120363](https://github.com/rust-lang/rust/issues/120363)

# Summary
Expand All @@ -20,7 +21,23 @@ to find potential definitions corresponding to uses within another function, or
not cross-reference those definitions at all.

Humans cross-referencing such uses and definitions may find themselves
similarly baffled.
similarly baffled by code such as the following:
```rust
trait Trait<T> {
fn method(&self) {}
}

struct Foo;

fn _foo() {
struct Bar;
impl Trait<Bar> for Foo {}
}

fn main() {
Foo.method();
}
```
Copy link
Member

@joshtriplett joshtriplett Apr 2, 2024

Choose a reason for hiding this comment

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

I'd prefer to leave this example out of the motivation section. Could you move it to the explanation section?


This change helps humans limit the scope of their search and avoid looking for
definitions inside other functions or items, without missing any relevant
Expand All @@ -30,23 +47,43 @@ subsequent Rust edtion, tools will be able to rely on this as well.
# Explanation
[explanation]: #explanation

The following types of items, "expression-containing items", can contain
expressions, including the definitions of other items:
An "expression-containing item" is defined as any
[expression](https://doc.rust-lang.org/reference/expressions.html) where an
[item](https://doc.rust-lang.org/reference/items.html) may be defined. For
example:
- Functions
- Closures
- The values assigned to `static` items or non-anonymous `const` items.
- The discriminant values assigned to `enum` variants

Rust will emit a warn-by-default lint for all of the following cases:
- An item nested inside an expression-containing item (through any level of
nesting) may not define an `impl Type` block unless the `Type` is also nested
inside the same expression-containing item.
- An item nested inside an expression-containing item (through any level of
nesting) may not define an `impl Trait for Type` unless either the `Trait` or
the `Type` is also nested inside the same expression-containing item.
- An item nested inside an expression-containing item (through any level of
nesting) may not define an exported macro visible outside the
expression-containing item (e.g. using `#[macro_export]`).
As an exception, anonymous `const` items are excluded from this
definition for the purposes of this RFC (see [unresolved-questions]),
as they're currently commonly used to define items within macros.

Rust will emit a warn-by-default lint when encountering an `impl` nested inside
an expression-containing item (through any level of nesting), unless any of the
following are true:
- For `impl T` or `impl TraitPath for T`, the definition of type `T` is also
nested inside the same expression-containing item.
- For `impl T<X, Y, ..>` or `impl TraitPath for T<X, Y, ..>`, the definition of
at least one of `T`, `X`, `Y`, .. is also nested inside the same expression-
containing item.
- For `impl Trait for TypePath` or `impl Trait<X, Y, ..> for TypePath`, the
definition of `Trait` is also nested inside the same expression-containing
item.
- For `impl Trait<X, Y, ..> for TypePath`, the definition of at least one of
`X`, `Y`, .. is also nested inside the same expression-containing item, *and*
the full path `Trait<X, Y, ..>` may not be inferred from outside the
expression-containing item, and further that such inferences on `Trait` would
not be permitted even in the absense of all impls nested in expression-
containing items.

For the purposes of the above rules, fundamental types such as `&X` and `&mut X`
are considered parameterized types (`T<X>`).

Rust will emit a warn-by-default lint when encountering an exported macro (e.g.
using `#[macro_export]`) nested inside an expression-containing item (through
any level of nesting).

In a future edition, we may consider making this lint deny-by-default, or
eventually making it a hard error. We'll evaluate the impact on the ecosystem
Expand Down Expand Up @@ -74,11 +111,11 @@ the signature without reflecting those properties in the signature.
# Unresolved questions
[unresolved-questions]: #unresolved-questions

We'll need a crater run to look at how widespread this pattern is in existing
code.
Should we flag these definitions in anonymous `const` items as well? This is
used in some macro expansions for
[compatibility reasons](https://github.com/rust-lang/rfcs/pull/3373#issuecomment-1885307786).

Should we flag these definitions in anonymous `const` items as well, or would
that produce unwanted warnings?
Is the last rule regarding parameterized trait `impl` items viable to implement?

# Future possibilities
[future-possibilities]: #future-possibilities
Expand Down