in Swift 6, it became illegal to shadow generic parameters from outer scopes:
struct S<T>
{
func f<T>() -> T
// ~
// Generic parameter 'T' shadows generic parameter from outer scope with the same name
{
let t:T = { fatalError() }()
return t
}
}
however, it appears it is still legal to shadow the Self type:
struct S<T>
{
func f<Self>() -> Self
{
let t:Self = { fatalError() }()
return t
}
}
In this example, you declare a generic parameter called Self, which doesn't shadow a generic parameter of the same name. The way I understand it is the generic parameters and declared types are separate entities, and the Self declared type is just a typealias for the declaration type. You're returning a type of the generic parameter in your function, not the Self type. You aren't shadowing it either because one is a generic parameter and the other is a declared type. Ergo, not illegal logically.
As a side node, you should avoid naming a generic parameter after a declared type with the same name or Self. That looks very wrong.
I could be convinced either way to disallow certain names for a generic parameter. In my opinion this should be allowed, but show a compiler warning about this edge case.
Here's a modified example that confirms that the usual meaning of Self here is shadowed away, and it's just behaving as if it were some other name like T.
struct S {
func f<Self: BinaryInteger>() -> [Self] {
return [.zero]
}
}
let result: [Int] = S().f() // Note result is `[Int]`, not `[S]`.
print(result) // => [0]
From a quick glance, I suspect this occurs because genericParamDecls doesn't include Self, so there's no shadowing ever detected by this code:
I agree that it looks like a bug. However the Selftype doesn't actually exist and all references to it get replaced by the declared type at compile-time. This edge case makes it appear to behave abnormally, even shadowing the Self type, but it makes complete sense logically. Which is why it should have a compiler warning.
This is necessary in order to support the very important feature of being able to use Self here as well:
struct S<Self> {
// Compiles.
var itIsImportantToSupportThis: S<Self> { self }
// Cannot convert return expression of type 'S<Self>' to return type 'Self'
var nobodyNeedsSelfUnlessItIsAGenericTypeParameter: Self { self }
}
The following is an even more valuable construct, because it protects the type so well that it can't be referred to within itself (without utilizing the module name).