Skip to content

NLL accepts higher-ranked subtype that non-NLL rejects #57642

Closed

Description

@matthewjasper

The following code example (playground)

// #![feature(nll)]

trait X {
    type G;
    fn make_g() -> Self::G;
}

impl<'a> X for fn(&'a ()) {
    type G = &'a ();

    fn make_g() -> Self::G {
        &()
    }
}

trait Y {
    type F;
    fn make_f() -> Self::F;
}

impl<T> Y for fn(T) {
    type F = fn(T);
    
    fn make_f() -> Self::F {
        |_| {}
    }
}

// Always an error, but the message is bad
fn higher_ranked_region_has_lost_its_binder() {
    let x = <fn (&())>::make_g();
}

// Works, but only with feature(nll)
fn magical() {
    let x = <fn (&())>::make_f();
}

produces the output

Details
error[E0308]: mismatched types
  --> src/lib.rs:31:13
   |
31 |     let x = <fn (&())>::make_g();
   |             ^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `X`
              found type `X`

error[E0308]: mismatched types
  --> src/lib.rs:36:13
   |
36 |     let x = <fn (&())>::make_f();
   |             ^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected type `Y`
              found type `Y`

The second example also unexpectedly compiles in full NLL mode.

Activity

  1. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    A-NLLArea: Non-lexical lifetimes (NLL)
    on Jan 15, 2019
  2. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jan 16, 2019
  3. changed the title [-]Unclear errors when higher ranked lifetime escapes its binder[/-] [+]NLL accepts higher-ranked subtype that non-NLL rejects[/+] on Jan 17, 2019
  4. nikomatsakis commented on Jan 17, 2019

    @nikomatsakis
    Contributor

    I renamed the title to focus on the part I would most like to investigate, which is why this function works but only with feature-nll:

    // Works, but only with feature(nll)
    fn magical() {
        let x = <fn (&())>::make_f();
    }

    That seems like a blocker to disabling migration mode.

  5. added
    NLL-soundWorking towards the "invalid code does not compile" goal
    on Jan 19, 2019
  6. self-assigned this
    on Jan 23, 2019
  7. pnkfelix commented on Feb 27, 2019

    @pnkfelix
    Contributor
  8. pnkfelix commented on Feb 27, 2019

    @pnkfelix
    Contributor

    marking P-medium to reflect that this is solely about #![feature(nll)], not migration mode (#58781). When the latter becomes higher priority, this will likewise be raised up.

  9. matthewjasper commented on Mar 27, 2019

    @matthewjasper
    ContributorAuthor

    Update: this is only an issue now with -Zno-leak-check

  10. pnkfelix commented on Mar 29, 2019

    @pnkfelix
    Contributor

    cc blocks #59490

  11. added
    E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.
    on May 4, 2019
  12. 1 remaining item

  13. oli-obk commented on May 24, 2019

    @oli-obk
    Contributor

    Diagnostics triage: this is not a diagnostics issue

  14. Aaron1011 commented on Aug 16, 2019

    @Aaron1011
    Contributor

    This is now a compiler error with -Z no-leak-check:

    error: higher-ranked subtype error
      --> test.rs:31:13
       |
    31 |     let x = <fn (&())>::make_g();
       |             ^^^^^^^^^^^^^^^^^^^^
    
  15. matthewjasper commented on Aug 16, 2019

    @matthewjasper
    ContributorAuthor

    Yes, but it should be two compiler errors, one in make_f and one on make_g.

  16. Aaron1011 commented on Aug 18, 2019

    @Aaron1011
    Contributor

    Looking at this further - why don't we want magical to compile? It looks perfectly correct to me.

  17. nikomatsakis commented on Oct 9, 2019

    @nikomatsakis
    Contributor

    Running this example with my branch from #65232 yields, among some other errors:

    error: implementation of `X` is not general enough
      --> /home/nmatsakis/tmp/issue-57642.rs:31:13
       |
    3  | / trait X {
    4  | |     type G;
    5  | |     fn make_g() -> Self::G;
    6  | | }
       | |_- trait `X` defined here
    ...
    31 |       let x = <fn (&())>::make_g();
       |               ^^^^^^^^^^^^^^^^^^ implementation of `X` is not general enough
       |
       = note: `X` would have to be implemented for the type `for<'r> fn(&'r ())`
       = note: ...but `X` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
    
    error: implementation of `Y` is not general enough
      --> /home/nmatsakis/tmp/issue-57642.rs:36:13
       |
    16 | / trait Y {
    17 | |     type F;
    18 | |     fn make_f() -> Self::F;
    19 | | }
       | |_- trait `Y` defined here
    ...
    36 |       let x = <fn (&())>::make_f();
       |               ^^^^^^^^^^^^^^^^^^ implementation of `Y` is not general enough
       |
       = note: `Y` would have to be implemented for the type `for<'r> fn(&'r ())`
       = note: ...but `Y` is actually implemented for the type `fn(&'0 ())`, for some specific lifetime `'0`
    

    This error seems correct. If I had to guess the problem with NLL, though, I would guess that this is working because of the 'empty rule -- in other words, we are inferring Y to be implemented for fn(&'empty ()) and we are (not incorrectly) deducing that fn(&'empty()) = fn(&()). My branch rejects this because I specifically tried to disallow that. I am not entirely convinced that we should disallow this -- it's just that 'empty doesn't have a very natural analog in polonius (though you can make it make sense). In fact, the idea of 'empty is kind of useful, or could be, so it may be that we want to accept it.

    If we want to disallow this example, I think we'll have to extend NLL to have a similarly "graded" notion of 'empty. I imagine the way to do this would be to extend the set of elements to include "empty" elements in each universe we create, and then permit these empty elements get added to all the "free regions" in the root universe.

  18. Aaron1011 commented on May 27, 2020

    @Aaron1011
    Contributor

    On the latest nightly, passing -Z borrowck=mir -Z no-leak-check causes higher_ranked_region_has_lost_its_binder and magical to produce higher-ranked subtype error.

    I think this is now purely a diagnostic issue, rather than a soundness issue.

  19. added
    A-diagnosticsArea: Messages for errors, warnings, and lints
    and removed
    NLL-soundWorking towards the "invalid code does not compile" goal
    A-diagnosticsArea: Messages for errors, warnings, and lints
    on May 27, 2020
  20. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    on May 28, 2020
  21. added a commit that references this issue on Oct 23, 2020
    5923218
  22. added a commit that references this issue on Oct 26, 2020
    9d7db48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-NLLArea: Non-lexical lifetimes (NLL)A-diagnosticsArea: Messages for errors, warnings, and lintsC-enhancementCategory: An issue proposing an enhancement or a PR with one.E-needs-testCall for participation: An issue has been fixed and does not reproduce, but no test has been added.P-mediumMedium priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions