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() {