diff --git a/stdlib/public/core/CMakeLists.txt b/stdlib/public/core/CMakeLists.txt
index 5e44d5c14e69c..8e34b150b9b69 100644
--- a/stdlib/public/core/CMakeLists.txt
+++ b/stdlib/public/core/CMakeLists.txt
@@ -127,6 +127,7 @@ set(SWIFTLIB_SOURCES
Mirror.swift
Process.swift
SliceBuffer.swift
+ Tuple.swift.gyb
VarArgs.swift
Zip.swift
Prespecialized.swift
diff --git a/stdlib/public/core/Tuple.swift.gyb b/stdlib/public/core/Tuple.swift.gyb
new file mode 100644
index 0000000000000..efbe1ffb66ff7
--- /dev/null
+++ b/stdlib/public/core/Tuple.swift.gyb
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
+// Licensed under Apache License v2.0 with Runtime Library Exception
+//
+// See http://swift.org/LICENSE.txt for license information
+// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
+//
+//===----------------------------------------------------------------------===//
+
+// Generate comparison functions for tuples up to some reasonable arity.
+
+% for arity in range(2,7):
+% typeParams = [chr(ord("A")+i) for i in range(arity)]
+% tupleT = "({})".format(",".join(typeParams))
+
+% equatableTypeParams = ", ".join(["{} : Equatable".format(c) for c in typeParams])
+
+/// Returns `true` iff each component of `lhs` is equal to the corresponding
+/// component of `rhs`.
+@warn_unused_result
+public func == <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
+% ops = ["lhs.{} == rhs.{}".format(i,i) for i in range(arity)]
+ return ${" && ".join(ops)}
+}
+
+/// Returns `true` iff any component of `lhs` is not equal to the corresponding
+/// component of `rhs`.
+@warn_unused_result
+public func != <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
+% ops = ["lhs.{} != rhs.{}".format(i,i) for i in range(arity)]
+ return ${" || ".join(ops)}
+}
+
+% comparableTypeParams = ", ".join(["{} : Comparable".format(c) for c in typeParams])
+% for op in ["<", ">"]:
+% for opeq in ["", "="]:
+/// A [lexicographical order](https://en.wikipedia.org/wiki/Lexicographical_order)
+/// over tuples of `Comparable` elements.
+///
+/// Given two tuples `(a1,a2,…,aN)` and `(b1,b2,…,bN)`, the first tuple is
+/// `${op}${opeq}` the second tuple iff `a1 ${op} b1` or (`a1 == b1` and
+/// `(a2,…,aN) ${op}${opeq} (b2,…,bN)`).
+@warn_unused_result
+public func ${op}${opeq} <${comparableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
+% for i in range(arity-1):
+ if lhs.${i} != rhs.${i} { return lhs.${i} ${op} rhs.${i} }
+% end
+ return lhs.${arity-1} ${op}${opeq} rhs.${arity-1}
+}
+% end
+% end
+% end
diff --git a/test/1_stdlib/Tuple.swift.gyb b/test/1_stdlib/Tuple.swift.gyb
new file mode 100644
index 0000000000000..e3b3201b4a3e7
--- /dev/null
+++ b/test/1_stdlib/Tuple.swift.gyb
@@ -0,0 +1,154 @@
+// RUN: rm -f %t.swift %t.out
+
+// RUN: %S/../../utils/gyb %s -o %t.swift
+// RUN: %S/../../utils/line-directive %t.swift -- %target-build-swift %t.swift -o %t.out
+// RUN: %S/../../utils/line-directive %t.swift -- %target-run %t.out
+// REQUIRES: executable_test
+
+import StdlibUnittest
+
+var TupleTestSuite = TestSuite("Tuple")
+
+// Test tuple comparison operators
+// all the tuple types use the same basic implementation for the operators
+// so testing any arity tests the logic for them all.
+// Include at least one invocation for all arities as a sanity check.
+
+% maxArity = 6 # the highest arity the operators are defined for
+
+func testEquality(
+ lhs: (A,B,C), equal: Bool, to rhs: (A,B,C),
+ //===--- TRACE boilerplate ----------------------------------------------===//
+ @autoclosure _ message: ()->String = "",
+ showFrame: Bool = true,
+ stackTrace: SourceLocStack = SourceLocStack(),
+ file: String = __FILE__, line: UInt = __LINE__
+) {
+ let trace = stackTrace.pushIf(showFrame, file: file, line: line)
+ expectEqual(equal, lhs == rhs, stackTrace: trace)
+ expectEqual(equal, rhs == lhs, stackTrace: trace)
+ expectEqual(!equal, lhs != rhs, stackTrace: trace)
+ expectEqual(!equal, rhs != lhs, stackTrace: trace)
+}
+
+TupleTestSuite.test("Tuple/equality") {
+ testEquality((1,2,3), equal: true, to: (1,2,3))
+ testEquality((1,2,3), equal: false, to: (1,2,4))
+ testEquality((1,2,3), equal: false, to: (1,3,3))
+ testEquality((1,2,3), equal: false, to: (2,2,3))
+ testEquality((1,"2",3), equal: true, to: (1,"2",3))
+ testEquality((1,"2",3), equal: false, to: (1,"3",3))
+ testEquality(("one", 2.2, 3..<5), equal: true, to: ("one", 2.2, 3..<5))
+
+ testEquality((1.0, 2.0, 3.0), equal: false, to: (1.0, 2.0, .NaN))
+ testEquality((1.0, 2.0, 3.0), equal: false, to: (1.0, .NaN, 3.0))
+ testEquality((1.0, 2.0, 3.0), equal: false, to: (.NaN, 2.0, 3.0))
+ testEquality((1.0, 2.0, 3.0), equal: false, to: (.NaN, .NaN, .NaN))
+ testEquality((1.0, 2.0, Float.NaN), equal: false, to: (1.0, 2.0, 3.0))
+ testEquality((1.0, 2.0, Float.NaN), equal: false, to: (1.0, 2.0, Float.NaN))
+ testEquality((Float.NaN, Float.NaN, Float.NaN), equal: false, to: (.NaN, .NaN, .NaN))
+ testEquality((Float.NaN, Float.NaN, Float.NaN), equal: false, to: (1.0, 2.0, 3.0))
+
+ expectTrue((1,2) == (1,2))
+ expectTrue((1,2) != (1,3))
+ expectTrue((1,2,3,4) == (1,2,3,4))
+ expectTrue((1,2,3,4) != (1,2,3,3))
+ expectTrue((1,2,3,4,5) == (1,2,3,4,5))
+ expectTrue((1,2,3,4,5) != (1,2,3,4,4))
+ expectTrue((1,2,3,4,5,6) == (1,2,3,4,5,6))
+ expectTrue((1,2,3,4,5,6) != (1,2,3,4,5,5))
+}
+
+TupleTestSuite.test("Tuple/equality/sanity-check") {
+ // sanity check all arities
+% for arity in range(2,maxArity+1):
+% a = str(tuple(range(1, arity+1)))
+% b = "({}, 0)".format(", ".join([str(i) for i in range(1,arity)]))
+% c = "(0, {})".format(", ".join([str(i) for i in range(2,arity+1)]))
+ expectTrue(${a} == ${a})
+ expectTrue(${a} != ${b})
+ expectTrue(${b} != ${a})
+ expectTrue(${a} != ${c})
+ expectTrue(${c} != ${a})
+% end
+}
+
+enum Ordering : Equatable {
+ case LessThan
+ case EqualTo
+ case GreaterThan
+ case UnorderedWith // Comparable defines strict total order, but Float disobeys that with NaN
+
+ var isLT: Bool {
+ return self == .LessThan
+ }
+ var isEQ: Bool {
+ return self == .EqualTo
+ }
+ var isGT: Bool {
+ return self == .GreaterThan
+ }
+}
+
+func testOrdering(
+ lhs: (A,B,C), _ ordering: Ordering, _ rhs: (A, B, C),
+ //===--- TRACE boilerplate ----------------------------------------------===//
+ @autoclosure _ message: ()->String = "",
+ showFrame: Bool = true,
+ stackTrace: SourceLocStack = SourceLocStack(),
+ file: String = __FILE__, line: UInt = __LINE__
+) {
+ let trace = stackTrace.pushIf(showFrame, file: file, line: line)
+ expectEqual(ordering.isLT, lhs < rhs, stackTrace: trace)
+ expectEqual(ordering.isLT, rhs > lhs, stackTrace: trace)
+ expectEqual(ordering.isLT || ordering.isEQ, lhs <= rhs, stackTrace: trace)
+ expectEqual(ordering.isLT || ordering.isEQ, rhs >= lhs, stackTrace: trace)
+ expectEqual(ordering.isGT, lhs > rhs, stackTrace: trace)
+ expectEqual(ordering.isGT, rhs < lhs, stackTrace: trace)
+ expectEqual(ordering.isGT || ordering.isEQ, lhs >= rhs, stackTrace: trace)
+ expectEqual(ordering.isGT || ordering.isEQ, rhs <= lhs, stackTrace: trace)
+}
+
+TupleTestSuite.test("Tuple/comparison") {
+ testOrdering((1,2,3), .EqualTo, (1,2,3))
+ testOrdering((1,2,3), .LessThan, (1,2,4))
+ testOrdering((1,2,3), .GreaterThan, (1,2,2))
+ testOrdering((1,3,2), .GreaterThan, (1,2,3))
+ testOrdering((0,2,3), .LessThan, (1,2,3))
+ testOrdering((3,2,1), .GreaterThan, (1,2,3))
+
+ testOrdering(("one", 2, 3.3), .EqualTo, ("one", 2, 3.3))
+ testOrdering(("one", 2, 3.3), .LessThan, ("one", 2, 3.4))
+ testOrdering(("on", 2, 3.3), .LessThan, ("one", 1, 3.2))
+
+ testOrdering((1, 2, Float.NaN), .UnorderedWith, (1, 2, .NaN))
+ testOrdering((1, Float.NaN, 3), .UnorderedWith, (1, 2, 3))
+ testOrdering((Double.NaN, 2, 3), .UnorderedWith, (.NaN, 2, 3))
+ testOrdering((Float.NaN, Float.NaN, Float.NaN), .UnorderedWith, (1, 2, 3))
+ testOrdering((1, 2, 3.0), .UnorderedWith, (1, 2, .NaN))
+ testOrdering((1, 2, 3.0), .LessThan, (1, 3, .NaN))
+ testOrdering((1, 2, 3.0), .GreaterThan, (1, 1, .NaN))
+ testOrdering((1, 2.0, 3), .LessThan, (2, .NaN, 3))
+ testOrdering((1, 2.0, 3), .GreaterThan, (0, .NaN, 3))
+ testOrdering((1, 2, Float.NaN), .LessThan, (1, 3, 3.0))
+ testOrdering((1, Float.NaN, 3), .GreaterThan, (0, 2.0, 3))
+ testOrdering(("one", "two", 3.0), .GreaterThan, ("a", "b", .NaN))
+ testOrdering(("one", "two", .NaN), .GreaterThan, ("a", "b", 3.0))
+ testOrdering((1.0, "two", "three"), .UnorderedWith, (.NaN, "two", "four"))
+ testOrdering((.NaN, "two", "three"), .UnorderedWith, (1.0, "two", "four"))
+}
+
+TupleTestSuite.test("Tuple/comparison/sanity-check") {
+ // sanity check all arities
+% for arity in range(2,maxArity+1):
+% a = str(tuple(range(1, arity+1)))
+% b = "({}, 0)".format(", ".join([str(i) for i in range(1,arity)]))
+% c = "(0, {})".format(", ".join([str(i) for i in range(2,arity+1)]))
+ expectTrue(${b} < ${a})
+ expectTrue(${b} <= ${a})
+ expectTrue(${a} > ${c})
+ expectTrue(${a} >= ${c})
+% end
+}
+
+runAllTests()
diff --git a/test/IDE/complete_expr_tuple.swift b/test/IDE/complete_expr_tuple.swift
index 4ddf870c7c7e0..93e309ac9dae4 100644
--- a/test/IDE/complete_expr_tuple.swift
+++ b/test/IDE/complete_expr_tuple.swift
@@ -27,27 +27,45 @@ func testTupleNoDot1() {
var t = (1, 2.0)
t#^TUPLE_NO_DOT_1^#
}
-// TUPLE_NO_DOT_1: Begin completions, 2 items
-// TUPLE_NO_DOT_1-NEXT: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}}
-// TUPLE_NO_DOT_1-NEXT: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_1: Begin completions, 8 items
+// TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .0[#Int#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_1-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
// TUPLE_NO_DOT_1-NEXT: End completions
func testTupleNoDot2() {
var t = (foo: 1, bar: 2.0)
t#^TUPLE_NO_DOT_2^#
}
-// TUPLE_NO_DOT_2: Begin completions, 2 items
-// TUPLE_NO_DOT_2-NEXT: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
-// TUPLE_NO_DOT_2-NEXT: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_2: Begin completions, 8 items
+// TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Pattern/CurrNominal: .bar[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_2-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
// TUPLE_NO_DOT_2-NEXT: End completions
func testTupleNoDot3() {
var t = (foo: 1, 2.0)
t#^TUPLE_NO_DOT_3^#
}
-// TUPLE_NO_DOT_3: Begin completions, 2 items
-// TUPLE_NO_DOT_3-NEXT: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
-// TUPLE_NO_DOT_3-NEXT: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_3: Begin completions, 8 items
+// TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .foo[#Int#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Pattern/CurrNominal: .1[#Double#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: == {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: <= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: >= {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: < {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: != {#(Int, Double)#}[#Bool#]{{; name=.+$}}
+// TUPLE_NO_DOT_3-DAG: Decl[InfixOperatorFunction]/OtherModule[Swift]: > {#(Int, Double)#}[#Bool#]{{; name=.+$}}
// TUPLE_NO_DOT_3-NEXT: End completions
func testTupleDot1() {