Skip to content

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

Open
@collinmay

Description

@collinmay

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-inferenceArea: Type inferenceA-type-systemArea: Type systemC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-typesRelevant to the types 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