rust-lang / rust-lang/rust

Unable to infer fn item to fn ptr coercion when fn ptr nested within more than one level of generics

Open
#119,605 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-inference A-type-system C-bug T-compiler T-types
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

I've run into a case where rustc is not able to infer a fn item to fn pointer coercion when the resulting type is sufficiently complex.

Playground

use std::iter;

pub struct A;
pub struct B<'a>(&'a A);

pub fn mapper(a: &A) -> B<'_> {
    B(a)
}

type MapIterator<'a> = iter::Map<std::slice::Iter<'a, A>, fn(&'a A) -> B<'a>>;
type CompoundIterator<'a> = iter::Chain<std::option::IntoIter<B<'a>>, MapIterator<'a>>;

// compiles
pub fn make_map_iterator<'a>(vec: &'a Vec<A>) -> MapIterator<'a> {
    vec.iter().map(mapper)
}

// doesn't compile
pub fn make_chain_iterator<'a>(option: Option<B<'a>>, vec: &'a Vec<A>) -> CompoundIterator<'a> {
    option.into_iter().chain(vec.iter().map(mapper))
}

The error I see is as follows:

error[E0308]: mismatched types
  --> src/lib.rs:20:5
   |
19 | pub fn make_chain_iterator<'a>(option: &'a Option<A>, vec: &'a Vec<A>) -> CompoundIterator<'a> {
   |                                                                           -------------------- expected `std::iter::Chain<std::option::IntoIter<B<'a>>, Map<std::slice::Iter<'a, A>, fn(&'a A) -> B<'a>>>` because of return type
20 |     option.as_ref().map(mapper).into_iter().chain(vec.iter().map(mapper))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found fn item
   |
   = note: expected struct `std::iter::Chain<std::option::IntoIter<B<'a>>, Map<std::slice::Iter<'a, _>, fn(&'a A) -> B<'a>>>`
              found struct `std::iter::Chain<std::option::IntoIter<B<'_>>, Map<std::slice::Iter<'_, _>, for<'a> fn(&'a A) -> B<'a> {mapper}>>`

The first example correctly infers that mapper needs to be coerced to fn ptr for the call to map to return the correct type. However, once the chain is introduced, the compiler no longer recognizes that mapper can (and should) be coerced to fn ptr. Once the fn ptr is buried in that second level of generics, the coercion is no longer inferred.

Changing .map(mapper) to .map(mapper as _) makes it coerce correctly, but it seems weird to me that it would be necessary to include that. It's as if the type inference isn't realizing coercion is an option here without being explicitly hinted.

The issue still occurs if I separate the 'a lifetime from the function pointer type using the for<'_> syntax suggested by the diagnostic. These two facts make me think this is not a lifetime issue.

I know this is goofy code and it's a mouthful to name these compound iterator types explicitly. This is reduced from code I wrote before RPITIT was stabilized and I needed to give these types names. At this point, for my application, I could replace this with RPITIT or use the as _ workaround, but I figured this was surprising enough to be worth asking about, and might uncover a bug.

Meta

rustc --version --verbose:

rustc 1.77.0-nightly (f688dd684 2024-01-04)
binary: rustc
commit-hash: f688dd684faca5b31b156fac2c6e0ae81fc9bc90
commit-date: 2024-01-04
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.6

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the coercion failure from the linked Playground using the reported rustc 1.77.0-nightly version and the example in src/lib.rs. Investigate how fn item-to-pointer coercion is inferred through nested iterator generics; done means the example compiles without the explicit as _ workaround and has a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.