What's wrong with this function (PartialKeyPath)?

Sorry for a title, but I couldn't invent a better one. And I have no good experience in asking for help.
I try to exchange data between two different models. (I know code is bit ugly)

let advanceWidthVertices = glyphLayers.map { flLayer in
    HyperVertex (value: flLayer.advanceWidth,
                 at:  layersCoordinates[flLayer.name!] ?? [],
                 strength:  hyperMaster.masterLocations.keys.contains(where: { n in n == flLayer.name})
                            ? 0 : 1)
self.advanceWidth = HyperValue(vertices: advanceWidthVertices, axes: axes)

It works pretty well, but because I have a lot of properties to exchange I would like to have a one function like this:

func getVerticesFor<T:SpaceIntepolatableProtocol>(masters:[String],
                                                  layers: [FontLabLayer],
                                                  layersCoordinates: [String:[Coordinate]],
                                                  path: PartialKeyPath<FontLabLayer>)
-> [HyperVertex<T, Coordinate>] {
    return layers.map { flLayer in
        HyperVertex (value:  flLayer[keyPath: path] as! T,
                     at:  layersCoordinates[flLayer.name!] ?? [],
                     strength:  masters.contains(where: { n in n == flLayer.name})
                                ? 0 : 1)

and call it:

self.advanceWidth = HyperValue(vertices: getVerticesFor(masters: hyperMaster.masterLocations.keys,
                   layers: glyphLayers,
                   layersCoordinates: layersCoordinates,
                   path: \.advanceWidth), 
           axes: axes)
//repeat many times for each property

But calling this function always give me an error "Type of expression is ambiguous without more context". Even in simplest possible form, with parameters taken from scope.

let vertices:[HyperVertex<Double, Coordinate>] = getVertices(path: \.advanceWidth)

How to solve this?

You've shown us the code with the problem, and the specific error message that you're getting. That's a good start!

Next step would be to make it self-contained, which means to include all the code that is needed to reproduce the bug in a single snippet. That way we can easily copy-paste it to a playground or something, and check if we also are getting the same error

protocol SpaceIntepolatableProtocol {}
struct FontLabLayer {
    var advanceWidth: Int
struct Coordinate {}
struct HyperVertex<A, B> {}
extension Double: SpaceIntepolatableProtocol {}

func getVerticesFor<T:SpaceIntepolatableProtocol>(
    layers: [FontLabLayer],
    layersCoordinates: [String:[Coordinate]],
    path: PartialKeyPath<FontLabLayer>)
-> [HyperVertex<T, Coordinate>] {
    return []

let vertices: [HyperVertex<Double, Coordinate>] = getVerticesFor( // Type of expression is ambiguous without more context
    masters: [],
    layers: [],
    layersCoordinates: [:],
    path: \.advanceWidth

Next step would be to make the sample minimal. How much can you cut out while still triggering that error?

func getVerticesFor(path: PartialKeyPath<Int>) {}
getVerticesFor(path: \.magnitude) // Type of expression is ambiguous without more context

This example is much simpler. It has no protocols, no arrays, no dictionaries, no generics, no return types, no custom types, and only a single parameter. It's much easier to see what's going on.

It looks like a bug to me, can you post it to bugs.swift.org ?

1 Like

Yes, of course. May I use your code as an example?

1 Like


Thank you very much!

1 Like

I have no idea I did it right ;) [SR-14549] Function call gives an error "Type of expression is ambiguous without more context". · Issue #56901 · apple/swift · GitHub

You were supposed to use the short example not the long one :sweat_smile:

The example you used is good, but is not great. It shows the bug, but 90% of it is useless code that can be removed and is just confusing the person trying to find out what the bug is.

You should also write when that bug happens. Does it happen for all function calls? When the parameter is any variant of a keypath? When the parameter is specifically a PartialKeyPath? When it's thursday? Spoiler: It's when parameter is a PartialKeyPath, and you pass in a key path literal to the function

That info would be useful to a person trying to solve this problem


We have seen that before on SR-5667, SR-12390, and in a few other duplicated issues. But in short is indeed a compiler issue while inferring the root type for the key path literal. There were some discussions some time ago about that in SR-12390 jira link so you can check there for more details :)