Cannot convert parent type X to expected type Y

Hi all,

I'm encountering these compiler errors, which I don't understand:

@ func fail1 : let x =
Cannot convert parent type 'project.Graphing<T>' to expected type 'project.Graphing<T>'
@ func fail2 : let x =
Cannot convert parent type Graphing<U>' to expected type Graphing<T>'

Could anyone please explain what's going on, and the appropriate way to express the static functions below?

Many thanks.

import Foundation

protocol Something {}

struct Graphing<T:Something>
{

    let vertices : [ Graphing.Vertex<T> ]
    
    struct Vertex<T:Something>
    {
        let placeholder = true
    }

    struct Region<T:Something>
    {

        let placeholder = true

        static func fail1( _ graph : Graphing<T> ) -> [ Graphing.Region<T> ]
        {
            let x : [ Graphing.Vertex<T> ] = graph.vertices
        }

        static func fail2<U:Something>( _ graph : Graphing<U> ) -> [ Graphing.Region<U> ]
        {
            let x : [ Graphing.Vertex<U> ] = graph.vertices
        }

    }

}

Change it to this


protocol Something {}

struct Graphing<T:Something>
{
	let vertices : [ Graphing.Vertex<T> ]
	
	struct Vertex<T1:Something>
	{
		let placeholder = true
	}

	struct Region<T:Something>
	{

		let placeholder = true

		static func fail1( _ graph : Graphing<T> ) -> [ Graphing.Region<T> ]
		{
			let x : [ Graphing.Vertex<T> ] = graph.vertices
		}
	}
}

The above seems to work. Changing the definition of Vertex to use T1 instead of T stops the confusion between the types. You don't need to use T as it picks that up from the definition of vertices in the Graphing struct.

The source of all the errors is the fact that you've defined multiple generic types with the same name T.

Don't do that.

2 Likes

Thank you both for your insights.

I assumed (incorrectly, but I don't think unreasonably) that a nested generic declaration <XXX:YYY>{} introduced a new type scope, in a similar manner to:

if let x : Int? = 1
{
   let x : Float = 2.0
}

@JohnBlackburne - your approach compiles succesfully, although I'm uncertain of the rules making it work.

@Peter-Schorn - I tried your suggestion of renaming types uniquely, but encountered the exact same compilation errors as before:

import Foundation
protocol Something {}
struct Graphing<T:Something>
{
    let vertices : [ Graphing.Vertex<T> ]
    struct Vertex<U:Something>
    {
        let placeholder = true
    }
    struct Region<V:Something>
    {
        let placeholder = true
        static func fail1( _ graph : Graphing<V> ) -> [ Graphing.Region<V> ]
        {
            let x : [ Graphing.Vertex<V> ] = graph.vertices
        }
        static func fail2<W:Something>( _ graph : Graphing<W> ) -> [ Graphing.Region<W> ]
        {
            let x : [ Graphing.Vertex<W> ] = graph.vertices
        }
    }
}

I believe there is more to understand. Do you know of any documentation explaining generic type scoping and propagation within hierarchies? This topic is not covered within the standard docs: Generics — The Swift Programming Language (Swift 5.7)

It works as defining Vertex as

struct Vertex<T1:Something>
{
	let placeholder = true
}

tells it use the type T1, providing it conforms to Something. Then this line

let vertices : [ Graphing.Vertex<T> ]

tells it the type is an array of Vertex, and tells it to use T which is the same T as the Graphing struct, as the type for the Vertex struct. I.e. the T1 gets replaced by T by that line, so you don't need to use T yourself in the Vertex definition.

It's a bit hard to offer you broader advice how to structure it as it's hard to tell what you're trying to do: what sort of data is T, what is Something, and what will replace the placeholders? The closest thing to that in my code I don't use a hierarchy, just separate structs. But I also don't use generics for vertex or related data, as they're all very concrete.

With different names, the diagnostics can now tell you exactly what is going on. In fail1, you have an argument of type Graphing<V> named graph. You access a member vertices, which has type [Graphing<V>.Vertex<V>]. You then assign it to a variable x of type [Graphing<T>.Vertex<V>], but T is not V.

Perhaps you are unfamiliar with the shorthand that Swift allows you to use within the scope of a type. Inside Graphing<T>, when you write Graphing without generic parameters, it is a shorthand for Graphing<T>.

Or perhaps you are unaware that nested types capture and are parameterized by the generic parameters of the parent type. That is, there isn't one type Graphing.Region<V>, but rather every type Graphing<A>.Region<V>, Graphing<B>.Region<V>, etc. is distinct.

Another way which might be clearer is strip out all your references to T inside Graphing. I mean like this.

protocol Something {}
struct Graphing<T:Something>
{
	let vertices : [ Graphing.Vertex ]
	struct Vertex
	{
		let placeholder = true
		let elements: [T] = []
	}
	struct Region
	{
		let placeholder = true
		static func fail1( _ graph : Graphing ) -> [ Graphing.Region]
		{
			let x : [ Graphing.Vertex ] = graph.vertices
			return []
		}
	}
}

You then will use T, inside Vertex and perhaps other places, to fill in its contents, as I've indicated. Vertex will use the T from Graphing to implement the contents, so you don't need to include `'T' in its definition or where it's later used.

Outside of the Graphing struct though you need to refer to Vertex through it belonging to Graphing and from that it gets the T to use. E.g.

struct Foo: Something {}

let v: Graphing<Foo>.Vertex = .init()
let g = Graphing<Foo>(vertices: [v])
let r = Graphing<Foo>.Region.fail1(g)

At last it makes sense to me!

@xwu - the shorthand is exactly what I was missing, thank you.

@JohnBlackburne - your example is very clear and helpful - thank you.