Skip to content

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

@roboz0r

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}"

Metadata

Metadata

Assignees

No one assigned

    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

    No fields configured for Bug.

    Projects

    Status

    New

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions