Skip to content

Commit d2f6959

Browse files
Add availability information to the new Math function protocols (#24187)
* Add availability information to the new Math function protocols The protocols ElementaryFunctions, RealFunctions, and Real are new in Swift 5.1 and accordingly need to have availability attached to them for platforms that are ABI-stable. The actual implementation hooks (static functions) are unconditionally defined on scalar types and marked @_alwaysEmitIntoClient, so they are available even when targeting older library versions, but the protocols themselves, and anything defined in terms of them (the global functions and the SIMD extensions) is only available when targeting library versions that have the new protocols. * Additionally provide concrete implementations of signGamma for each stdlib-builtin floating-point type. * Remove Real[Functions] protocols pending re-review Temporarily pull these back so we can make minor tweaks to the design and get a re-review on SE.
1 parent fa5d66c commit d2f6959

File tree

4 files changed

+75
-87
lines changed

4 files changed

+75
-87
lines changed

stdlib/public/Darwin/CoreGraphics/CGFloat.swift.gyb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ public func %=(lhs: inout CGFloat, rhs: CGFloat) {
515515

516516
%from SwiftMathFunctions import *
517517

518-
extension CGFloat: Real {
518+
extension CGFloat: ElementaryFunctions {
519519
% for func in ElementaryFunctions + RealFunctions:
520520

521521
@_alwaysEmitIntoClient
@@ -553,12 +553,20 @@ extension CGFloat: Real {
553553
return CGFloat(NativeType.atan2(y: y.native, x: x.native))
554554
}
555555

556-
#if !os(Windows)
557556
@_alwaysEmitIntoClient
558557
public static func logGamma(_ x: CGFloat) -> CGFloat {
559558
return CGFloat(NativeType.logGamma(x.native))
560559
}
561-
#endif
560+
561+
@_alwaysEmitIntoClient
562+
public static func signGamma(_ x: CGFloat) -> FloatingPointSign {
563+
if x >= 0 { return .plus }
564+
let trunc = x.rounded(.towardZero)
565+
if x == trunc { return .plus }
566+
let halfTrunc = trunc/2
567+
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
568+
return .plus
569+
}
562570
}
563571

564572
//===----------------------------------------------------------------------===//

stdlib/public/core/MathFunctions.swift.gyb

Lines changed: 59 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import SwiftShims
3434
/// ElementaryFunctions and FloatingPoint.
3535
///
3636
/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function
37+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
3738
public protocol ElementaryFunctions {
3839

3940
%for func in ElementaryFunctions:
@@ -59,77 +60,12 @@ public protocol ElementaryFunctions {
5960
static func root(_ x: Self, _ n: Int) -> Self
6061
}
6162

62-
/// A type that models the real numbers.
63-
///
64-
/// Conformance to this protocol means that all the FloatingPoint operations
65-
/// are available, as well as the ElementaryFunctions, plus the following
66-
/// additional math functions: atan2, erf, erc, hypot, tgamma.
67-
///
68-
/// logGamma and signGamma are also available on non-Windows platforms.
69-
public protocol Real: ElementaryFunctions, FloatingPoint {
70-
%for func in RealFunctions:
71-
72-
${func.comment}
73-
static func ${func.decl("Self")}
74-
%end
75-
76-
/// `atan(y/x)` with quadrant fixup.
77-
///
78-
/// There is an infinite family of angles whose tangent is `y/x`. `atan2`
79-
/// selects the representative that is the angle between the vector `(x, y)`
80-
/// and the real axis in the range [-π, π].
81-
static func atan2(y: Self, x: Self) -> Self
82-
83-
#if !os(Windows)
84-
// lgamma is not available on Windows.
85-
// TODO: provide an implementation of lgamma with the stdlib to support
86-
// Windows so we can vend a uniform interface.
87-
88-
/// `log(gamma(x))` computed without undue overflow.
89-
///
90-
/// `log(abs(gamma(x)))` is returned. To get the sign of `gamma(x)` cheaply,
91-
/// use `signGamma(x)`.
92-
static func logGamma(_ x: Self) -> Self
93-
#endif
94-
}
95-
96-
extension Real {
97-
#if !os(Windows)
98-
// lgamma is not available on Windows; no lgamma means signGamma
99-
// is basically useless, so don't bother exposing it.
100-
101-
/// The sign of `gamma(x)`.
102-
///
103-
/// This function is typically used in conjunction with `logGamma(x)`, which
104-
/// computes `log(abs(gamma(x)))`, to recover the sign information that is
105-
/// lost to the absolute value.
106-
///
107-
/// `gamma(x)` has a simple pole at each non-positive integer and an
108-
/// essential singularity at infinity; we arbitrarily choose to return
109-
/// `.plus` for the sign in those cases. For all other values, `signGamma(x)`
110-
/// is `.plus` if `x >= 0` or `trunc(x)` is odd, and `.minus` otherwise.
111-
@_alwaysEmitIntoClient
112-
public static func signGamma(_ x: Self) -> FloatingPointSign {
113-
if x >= 0 { return .plus }
114-
let trunc = x.rounded(.towardZero)
115-
// Treat poles as gamma(x) == +inf. This is arbitrary, but we need to
116-
// pick one sign or the other.
117-
if x == trunc { return .plus }
118-
// Result is .minus if trunc is even, .plus otherwise. To figure out if
119-
// trunc is even or odd, check if trunc/2 is an integer.
120-
let halfTrunc = trunc/2
121-
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
122-
return .plus
123-
}
124-
#endif
125-
}
126-
12763
%for type in all_floating_point_types():
12864
% if type.bits == 80:
12965
#if (arch(i386) || arch(x86_64)) && !os(Windows)
13066
% end
13167
% Self = type.stdlib_name
132-
extension ${Self}: Real {
68+
extension ${Self}: ElementaryFunctions {
13369
% for func in ElementaryFunctions + RealFunctions:
13470

13571
@_alwaysEmitIntoClient
@@ -172,9 +108,66 @@ extension ${Self}: Real {
172108
public static func logGamma(_ x: ${Self}) -> ${Self} {
173109
return _swift_stdlib_lgamma${type.cFuncSuffix}(x)
174110
}
111+
112+
@_alwaysEmitIntoClient
113+
public static func signGamma(_ x: ${Self}) -> FloatingPointSign {
114+
if x >= 0 { return .plus }
115+
let trunc = x.rounded(.towardZero)
116+
if x == trunc { return .plus }
117+
let halfTrunc = trunc/2
118+
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
119+
return .plus
120+
}
175121
#endif
176122
}
177123
% if type.bits == 80:
178124
#endif
179125
% end
180126
%end
127+
128+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
129+
extension SIMD where Scalar: ElementaryFunctions {
130+
% for func in ElementaryFunctions:
131+
132+
@_alwaysEmitIntoClient
133+
public static func ${func.decl("Self")} {
134+
var r = Self()
135+
for i in r.indices {
136+
r[i] = Scalar.${func.swiftName}(${func.params(suffix="[i]")})
137+
}
138+
return r
139+
}
140+
% end
141+
142+
@_alwaysEmitIntoClient
143+
public static func pow(_ x: Self, _ y: Self) -> Self {
144+
var r = Self()
145+
for i in r.indices {
146+
r[i] = Scalar.pow(x[i], y[i])
147+
}
148+
return r
149+
}
150+
151+
@_alwaysEmitIntoClient
152+
public static func pow(_ x: Self, _ n: Int) -> Self {
153+
var r = Self()
154+
for i in r.indices {
155+
r[i] = Scalar.pow(x[i], n)
156+
}
157+
return r
158+
}
159+
160+
@_alwaysEmitIntoClient
161+
public static func root(_ x: Self, _ n: Int) -> Self {
162+
var r = Self()
163+
for i in r.indices {
164+
r[i] = Scalar.root(x[i], n)
165+
}
166+
return r
167+
}
168+
}
169+
170+
%for n in [2,3,4,8,16,32,64]:
171+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
172+
extension SIMD${n}: ElementaryFunctions where Scalar: ElementaryFunctions { }
173+
%end

test/stdlib/MathFunctions.swift.gyb

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ func expectEqualWithTolerance<T>(_ expected: TestLiteralType, _ actual: T,
4545

4646
%from SwiftMathFunctions import *
4747

48+
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
4849
internal extension ElementaryFunctions where Self: BinaryFloatingPoint {
4950
static func elementaryFunctionTests() {
5051
/* Default tolerance is 3 ulps unless specified otherwise. It's OK to relax
@@ -78,21 +79,6 @@ internal extension ElementaryFunctions where Self: BinaryFloatingPoint {
7879
}
7980
}
8081

81-
internal extension Real where Self: BinaryFloatingPoint {
82-
static func realFunctionTests() {
83-
expectEqualWithTolerance(0.54041950027058415544357836460859991, Self.atan2(y: 0.375, x: 0.625))
84-
expectEqualWithTolerance(0.72886898685566255885926910969319788, Self.hypot(0.375, 0.625))
85-
expectEqualWithTolerance(0.4041169094348222983238250859191217675, Self.erf(0.375))
86-
expectEqualWithTolerance(0.5958830905651777016761749140808782324, Self.erfc(0.375))
87-
expectEqualWithTolerance(2.3704361844166009086464735041766525098, Self.gamma(0.375))
88-
#if !os(Windows)
89-
expectEqualWithTolerance( -0.11775527074107877445136203331798850, Self.logGamma(1.375), ulps: 16)
90-
expectEqual(.plus, Self.signGamma(1.375))
91-
expectEqual(.minus, Self.signGamma(-2.375))
92-
#endif
93-
}
94-
}
95-
9682
%for T in ['Float', 'Double', 'CGFloat', 'Float80']:
9783
% if T == 'Float80':
9884
#if (arch(i386) || arch(x86_64)) && !os(Windows)
@@ -102,8 +88,9 @@ internal extension Real where Self: BinaryFloatingPoint {
10288
% end
10389

10490
MathTests.test("${T}") {
105-
${T}.elementaryFunctionTests()
106-
${T}.realFunctionTests()
91+
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
92+
${T}.elementaryFunctionTests()
93+
}
10794
}
10895

10996
% if T in ['CGFloat', 'Float80']:

utils/SwiftMathFunctions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def decl(self, type):
1818
return self.swiftName + "(" + self.params("_ ", ": " + type) + \
1919
") -> " + type
2020

21-
def free_decl(self, constraint="T: ElementaryFunctions"):
21+
def freeDecl(self, constraint):
2222
return self.swiftName + "<T>(" + self.params("_ ", ": T") + \
2323
") -> T where " + constraint
2424

0 commit comments

Comments
 (0)