dotnet / dotnet/fsharp

Wrong type inference when using extension method for generic GetEnumerator

Open
#6,803 4 comments 0 reactions 1 assignee Claimed by @dsyme View on GitHub
Area-Compiler-Checking Bug Impact-Medium
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 11h
Merged PRs (30d)
131

Description

Given a generic type `GenericType<'T>` that supports the collection-pattern (`GetEnumerator` without `seq<_>`):

- using a generic enumerator (`Current` is `'T`)
- but defining `GetEnumerator` as an extension method,

iterating such collection with `for` causes the compiler to assign a fresh type to the extracted value, instead of inferring `'T`.

Both F#-style and C#-style extension methods fail the same way.

**Repro steps**

Minimal repro script:

```fsharp
type GenericType<'T> = { Value : 'T }

module OtherModule =
type Enumerator<'T>(gen : GenericType<'T>) =
member __.Current = gen.Value
member __.MoveNext() = true

type GenericType<'T> with
member this.GetEnumerator() = Enumerator(this)

// x should be int, but string is inferred
for x in { Value = 2 } do printfn "%s" x
```

**Expected behavior**

The extracted value `x` at the `for` should be inferred as the same type as the enumerator's `Current` property, `int`.

**Actual behavior**

The extracted value is inferred as a fresh type, allowing it to be constrained to the wrong type, `string`. According to the spec, this behaviour should only happen if `Current` has type `obj` and the collection type has an `Item` property of more specific type.

**Known workarounds**

Defining `GetEnumerator` as a non-extension method works correctly.

**Related information**

.NET Core SDK 3.0.100-preview5-011568

Found this while trying a proof of concept of Option enumerators, as an alternative to a closed language proposal.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.