You know, I really think this is doable. I've been playing with converting this branch, and it's so tedious to write that a few hours later I still haven't compiled it, but I haven't hit a blocker yet.
Basically, the design I'm playing with has each type conform to VectorizableN protocols corresponding to the vector sizes it supports:
protocol Vectorizable4 {
associatedtype _BuiltinVector4
static func _vector4ExtractElement(at i: Int, in vector: _BuiltinVector4) -> Self
static func _vector4InsertElement(_ value: Self, at i: Int, in vector: inout _BuiltinVector4)
static func _vector4(_ lhs: _BuiltinVector4, adding rhs: _BuiltinVector4) -> _BuiltinVector4
// And so on for all common operations.
}
protocol VectorizableInteger4: Vectorizable4, FixedWidthInteger {
static func _vector4(_ lhs: _BuiltinVector4, shiftedLeftBy rhs: _BuiltinVector4) -> _BuiltinVector4
// And so on for all integer-only operations.
}
protocol VectorizableFloatingPoint4: Vectorizable4, BinaryFloatingPoint {
static func _vector4(_ m: _BuiltinVector4, multipliedBy x: _BuiltinVector4, adding b: _BuiltinVector4) -> _BuiltinVector4
// And so on for all floating-point-only operations.
}
// Now do it again for Vectorizable8, Vectorizable16...
Then you have concrete VectorN<Element> types which basically just wrap these operations:
struct Vector4<Element: Vectorizable4>: RandomAccessCollection {
var _value: Element._BuiltinVector4
subscript(i: Int) -> Element {
get { return Element._vector4ExtractElement(at: i, in: _value) }
set { Element._vector4InsertElement(newValue, at: i, in: &_value) }
}
static func &+ (lhs: Vector4, rhs: Vector4) -> Vector4 {
return Vector4(Element._vector4(lhs._value, adding: rhs._value))
}
// etc.
}
extension Vector4 where Element: VectorizableInteger4 {
static func &<< (lhs: Vector4, rhs: Vector4) -> Vector4 {
return Vector4(Element._vector4(lhs._value, shiftedLeftBy: rhs._value))
}
// etc.
}
And finally each type conforms to the relevant protocols and provides the proper associated types and implementations. For instance, Int8 will set its _BuiltinVector4 to Builtin.Vec4xInt8, then provide implementations using Vec4xInt8 builtins.
This is a huge pain to write, but I think it ought to be workable. I am a little concerned that it might not inline as aggressively as I'd like, but it seems to me (although I haven't gotten far enough to test it yet!) that even if the complier doesn't do the right thing natively, we ought to be able to force the issue with @_specialize.
So, is there some reason this won't actually work? Because if it will work, it neatly sidesteps all of the naming and code completion pollution issues we've been discussing.