Skip to content

Higher-ranked fn pointers can be cast but no longer coerce #95132

Closed
@QuineDot

Description

@QuineDot

Code

I tried this code:

fn f<'long: 'short, 'short>(_: &'short &'long ()) {
    let higher_ranked: for<'any> fn(&'any ()) = |_| {};
    let long: fn(&'long ()) = |_| {};
    let short: fn(&'short()) = |_| {};
    let stat: fn(&'static ()) = |_| {};

    // Contravariance: `fn(&'short ())` is a subtype of `fn(&'long ())`, etc.
    let _: fn(&'static ()) = long;
    let _: fn(&'long ()) = short;

    // The higher-ranked type is a subtype of all of them
    let _: fn(&'static ()) = higher_ranked;
    let _: fn(&'long ()) = higher_ranked;
    let _: fn(&'short ()) = higher_ranked;
}

I expected to see this happen: Successful compilation

Instead, this happened: Compiler error:


error[E0308]: mismatched types
  --> <source>:12:12
   |
12 |     let _: fn(&'static ()) = higher_ranked;
   |            ^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `fn(&'static ())`
              found fn pointer `for<'any> fn(&'any ())`

error[E0308]: mismatched types
  --> <source>:13:12
   |
13 |     let _: fn(&'long ()) = higher_ranked;
   |            ^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `fn(&'long ())`
              found fn pointer `for<'any> fn(&'any ())`

error[E0308]: mismatched types
  --> <source>:14:12
   |
14 |     let _: fn(&'short ()) = higher_ranked;
   |            ^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `fn(&'short ())`
              found fn pointer `for<'any> fn(&'any ())`

Version it worked on

It most recently worked on: Rust 1.35.

It works on every version before that which I spot checked (but I did not spot check all of them).

Versions before 1.18.0 cannot coerce a closure to a function pointer, but this variation:

fn g(_: &()) {}
fn f<'long: 'short, 'short>(_: &'short &'long ()) {
    let higher_ranked: for<'any> fn(&'any ()) = g;

    // The higher-ranked type is a subtype of all of them
    let _: fn(&'static ()) = higher_ranked;
    let _: fn(&'long ()) = higher_ranked;
    let _: fn(&'short ()) = higher_ranked;
}

Works all the way back to Rust 1.0.0.

Versions with regression

Rust 1.36.0 and all those which I spot-checked after this version.

The error changes in Rust 1.56.0 due to one of the #57374 fixes; I didn't track down which.

Current playground, any version.

  • Stable version: 1.59.0
  • Beta version: 1.60.0-beta.5 (2022-03-18 6ee5a40)
  • Nightly version: 1.61.0-nightly (2022-03-18 1bfe40d)

Workaround

Casting still works:

    let _: fn(&'static ()) = higher_ranked as _;
    let _: fn(&'long ()) = higher_ranked as _;
    let _: fn(&'short ()) = higher_ranked as _;

But RFC 401 says this should not be necessary.

@rustbot modify labels: +regression-from-stable-to-stable -regression-untriaged

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.P-mediumMedium priorityregression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions