SRTP "member get_Item" causes "FS0192: internal error" when used on strings · Issue #18093 · dotnet/fsharp · GitHub
Skip to content

SRTP "member get_Item" causes "FS0192: internal error" when used on strings #18093

Closed

Description

@roboz0r

Please provide a succinct description of the issue.

I was attempting to index into array-like collections using SRTP and encountered the error using strings. It appears the underlying cause is that the indexer method is not necessarily get_Item and may be overridden using [<IndexerName>]. This is the case in String.cs with the attribute [IndexerName("Chars")] that results in a get_Chars method instead of get_Item.

Provide the steps required to reproduce the problem:

It can be reproduced using FSI

let inline indexInto (slice: ^T when ^T: (member get_Item: int -> ^U)) i : ^U =
    slice.get_Item i

let array = [| 1; 2; 3; 4; 5 |] // Ok
let list = ResizeArray<int>(array) // Ok
let str = "abcde" 

printfn $"{indexInto array 2}" // Ok
printfn $"{indexInto list 2}" // Ok
printfn $"{indexInto str 2}" // error FS0192: internal error: unknown builtin witness 'get_ItemDynamic'

let inline indexChars (slice: ^T when ^T: (member get_Chars: int -> ^U)) i : ^U =
    slice.get_Chars i

printfn $"{indexChars array 2}" // The type 'int array' does not support the operator 'get_Chars'
printfn $"{indexChars str 2}" // Ok

Expected behavior

My initial expectation was that indexInto would allow indexing into any type that had an indexer (using either get_Item or [i]) but after learning about [<IndexerName>] I'm no longer sure if this should be the case. Some suggestions for a resolution:

  1. Change the compiler's understanding of member get_Item in SRTP

Make get_Item a compiler-known method that checks for usage of [<IndexerName>] on the target type and inserts the appropriate call.

Despite being "magical" this is probably the expected behavior of most F# developers unaware of the existence of [<IndexerName>].

  1. Change the error and provide alternate mechanism to combine SRTP and indexers

The error should become "The type 'string' does not support the operator 'get_Item'" rather than "FS0192 internal error" to match other type errors.

Consider some other syntax to allow indexers to be accessed for all types using SRTP:

  • member []
  • member Item[]
  • [<Indexer>] member get_Item
  • member op_Item
let inline indexInto (slice: ^T when ^T: (member []: int -> ^U)) i : ^U =
    slice.[i] // or slice[i]

Known workarounds

  • Instead of SRTP, use interfaces to access elements (it's noteworthy that String doesn't implement IReadOnlyList which would seem appropriate for this case)
  • Use Span which unifies some indexable types but has other usage limitations
  • Wrap the string and re-implement the indexer
[<Struct>]
type Wrapper =
    | Wrapper of string
    member this.Item with get i = 
        let (Wrapper s) = this
        s.[i]

printfn $"{indexInto (Wrapper str) 2}"

Activity

  1. added this to the Backlog milestone on Nov 30, 2024
  2. T-Gro commented on Dec 2, 2024

    @T-Gro
    Member

    This is an unfortunate consequence on how indexer for strings is done.
    I don't think I have seen this used elsewhere, in any other common type.

    From the proposals, the member [] syntax looks good and would also enable dropping the get_Item workaround/limitation. For treating it as a bugfix though, I am afraid the audience for this issue is too small to justify a language change (happy to be corrected! ).

    Either a change of the compiler message, or a known compile-time replacement from get_Item into any method with the [<IndexerName>] attribute are more likely.

    Thank you for describing the issue in depth and listing good workarounds - this will be a nice reference in case anyone else encounters the limitation for the intersection of SRTP's and indexing into a string.

  3. added
    Area-Compiler-SRTPbugs in SRTP inference, resolution, witness passing, code gen
    Impact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.
    and removed on Dec 2, 2024
  4. T-Gro commented on May 18, 2026

    @T-Gro
    Member
    🤖 Copilot implementation hints

    Fix

    One-line typo fix in src/Compiler/TypedTree/TcGlobals.fs at TryMakeOperatorAsBuiltInWitnessInfo (~line 1932).

    The string case for get_Item matches [stringTy; _; _] (3 elements) but CompiledObjectAndArgumentTypes for string.get_Item(int) is [stringTy; intTy] (2 elements). It never matches, falls through to failwithf.

    Change:

    | "get_Item", [stringTy; _; _], _, [_; _] when isStringTy g stringTy ->

    to:

    | "get_Item", [stringTy; _], _, [_; _] when isStringTy g stringTy ->

    Instructions

    • Use TDD: write a test first that reproduces let inline indexInto (s: ^a) i = (^a: (member get_Item: int -> ^b) (s, i)) in indexInto "abc" 2 failing with FS0192, then fix.
    • Run related tests: dotnet test tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj --filter "SRTP" -c Debug
    • After code change, invoke the expert-review skill to review your work.
    • Minimal surgical fix. No bloated comments. This is an expert-level codebase.
    • Run dotnet fantomas . on changed files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Area-Compiler-SRTPbugs in SRTP inference, resolution, witness passing, code genBugImpact-Low(Internal MS Team use only) Describes an issue with limited impact on existing code.

Type

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions