Normally, if you write this code;
func test1() {
return CGPoint.zero
}
The compiler will tell you
unexpected non-void return value in void function
which is very nice. Everyone knows what they did wrong.
But this code will cause the compiler to say something different:
func test2() {
let a: CGFloat = 1
return CGPoint(x: 0.0, y: a)
}
The compiler now says:
cannot invoke initializer for type 'CGPoint' with an argument list of type '(x: Double, y: CGFloat)'
note: overloads for 'CGPoint' exist with these partially matching parameter lists: (x: CGFloat, y: CGFloat), (x: Double, y: Double)
Now I am confused.
Why doesn't the compiler mention anything about returning a value in a void function, and instead complain about calling CGPoint.init
with (x: 0.0, y: a)
, which is completely fine?
Since 0.0
is a float literal, and CGFloat
conforms to BinaryFloatingPoint
which conforms to ExpressibleByFloatLiteral
, 0.0
is converted to CGFloat
at compile time. As a result, the CGPoint(x: CGFloat, y: CGFloat)
initializer should have been used. Why does the compiler complain that CGPoint
can't be initialized with (x: Double, y: CGFloat)
?
I thought this was because the compiler saw that I was returning a value where I shouldn't and stopped evaulating the expression so 0.0
is not converted to CGFloat
. But if that's the case, the compiler should have said something about returning a value in a void function!
Is this a bug or intended?
CGPoint
has three overloaded initializers with the same argument names:
public init(x: CGFloat, y: CGFloat)
public init(x: Double, y: Double)
public init(x: Int, y: Int)
You'll get the same error message with
let v: Void = CGPoint(x: 1.0, y: 2)
// error: cannot invoke initializer for type 'CGPoint' with an argument list of type '(x: Double, y: Int)'
// note: overloads for 'CGPoint' exist with these partially matching parameter lists: (x: Int, y: Int), (x: Double, y: Double)
Here is another example demonstrating the effect:
struct A {
init(x: Double, y: Double) { }
}
struct B {
init(x: Double, y: Double) { }
init(xi: Int, yi: Int) { }
}
struct C {
init(x: Double, y: Double) { }
init(x: Int, y: Int) { }
}
let v1: Void = A(x: 1, y: 2.0)
// error: cannot convert value of type 'A' to specified type 'Void' (aka '()')
let v2: Void = B(x: 1, y: 2.0)
// error: cannot convert value of type 'B' to specified type 'Void' (aka '()')
let v3: Void = C(x: 1.0, y: 2.0)
// error: cannot convert value of type 'C' to specified type 'Void' (aka '()')
let v4: Void = C(x: 1, y: 2.0)
// error: cannot invoke initializer for type 'C' with an argument list of type '(x: Int, y: Double)'
Only in the last line, C(x: 1, y: 2.0)
matches two initializers.
I assume that the compiler tries to match against both, fails to convert
the return value to Void
in both cases, and therefore emits a different
error message.
The error message is not very helpful, so you might consider to file a bug report at http://bugs.swift.org.