Skip to content

Commit 1941c5e

Browse files
ericalattner
authored andcommitted
Updating size and stride functions in the stdlib. (Response to SE-0096) (swiftlang#350)
1 parent 9e0ea7e commit 1941c5e

File tree

1 file changed

+204
-0
lines changed

1 file changed

+204
-0
lines changed

proposals/XXXX-sidestride.md

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Renaming sizeof, sizeofValue, strideof, strideofValue, align, alignofValue to comply with API Guidelines
2+
3+
* Proposal: TBD
4+
* Author: [Erica Sadun](http://github.com/erica)
5+
* Status: TBD
6+
* Review manager: TBD
7+
8+
## Introduction
9+
10+
Upon accepting [SE-0096](https://github.com/apple/swift-evolution/blob/master/proposals/0096-dynamictype.md), the core team renamed the proposed stdlib function from `dynamicType()` to `type(of:)` to better comply with Swift's API guidelines.
11+
This proposal renames `sizeof`, `sizeofValue`, `strideof`, `strideofValue`, `align`, and `alignOf` to emulate SE-0096's example.
12+
13+
Swift Evolution Discussion: [\[Pitch\] Renaming sizeof, sizeofValue, strideof, strideofValue](http://thread.gmane.org/gmane.comp.lang.swift.evolution/19459)
14+
15+
[Earlier](http://thread.gmane.org/gmane.comp.lang.swift.evolution/15830)
16+
17+
## Motivation
18+
19+
Swift's API guidelines indicate that free-standing functions without side-effects should be named using a noun describing the returned value.
20+
21+
* Although `sizeof()`, etc are treated as terms of art, these names are appropriated from C. The functions do not correspond to anything named `sizeof` in LLVM.
22+
* All names are expanded to be more explanatory by prefixing `memory` and adopting lower camel case. Names are more often read than written, and the proposed names are more self-documenting.
23+
* As `stride` already has a well-established meaning in the standard library, this proposal changes its name to `interval`, matching existing documentation.
24+
* Via API guidance, `align` is renamed to `alignment`.
25+
* SE-0096's `type(of:)` signature operates on instances. This aligns it with `sizeofValue`, `alignofValue`, `strideofValue`, which operate on instances. Using `of` rather than `ofValue` matches this behavior but at the cost of clarity. This proposal recommends amending SE-0096 to change `type(of:)` to `type(ofValue:)`.
26+
* Improving type-call usability should take precedence over instance-calls. (See next bullet point.) Although `function(ofType:)` offers a natural correspondence to SE-0096, this proposal recommends omitting a label to enhance readability. `memorySize` should be clear enough (and noun enough) to mitigate any issues of whether the name is or is not a noun.
27+
* As the following chart shows, type-based calls consistently outnumber instance-based calls in gist, github, and stdlib searches. The Google search for `sizeof` is probably too general based on its use in other languages.
28+
29+
<table>
30+
<tr width = 800>
31+
<th width = 200>Term</td>
32+
<th width = 150>stdlib search</td>
33+
<th width = 150>gist search</td>
34+
<th width = 150>Google site:github.com swift</td>
35+
</tr>
36+
<tr width = 800>
37+
<td width = 200>sizeof</td>
38+
<td width = 150>157</td>
39+
<td width = 150>169</td>
40+
<td width = 150>(18,600, term is probably too general)</td>
41+
</tr>
42+
<tr width = 800>
43+
<td width = 200>sizeofValue</td>
44+
<td width = 150>4</td>
45+
<td width = 150>34</td>
46+
<td width = 150>584</td>
47+
</tr>
48+
<tr width = 800>
49+
<td width = 200>alignof</td>
50+
<td width = 150>44</td>
51+
<td width = 150>11</td>
52+
<td width = 150>334</td>
53+
</tr>
54+
<tr width = 800>
55+
<td width = 200>alignofValue</td>
56+
<td width = 150>5</td>
57+
<td width = 150>5</td>
58+
<td width = 150>154</td>
59+
</tr>
60+
<tr width = 800>
61+
<td width = 200>strideof</td>
62+
<td width = 150>24</td>
63+
<td width = 150>19</td>
64+
<td width = 150>347</td>
65+
</tr>
66+
<tr width = 800>
67+
<td width = 200>strideofValue</td>
68+
<td width = 150>1</td>
69+
<td width = 150>5</td>
70+
<td width = 150>163</td>
71+
</tr>
72+
</table>
73+
74+
**Note:** There is a [known bug](https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20160530/002150.html) (cite D. Gregor) that does not enforce `.self` when used with `sizeof`, allowing `sizeof(UInt)`. This call should be `sizeof(UInt.self)`. This proposal is written as if the bug were resolved without relying on adoption of [SE-0090](https://github.com/apple/swift-evolution/blob/master/proposals/0090-remove-dot-self.md).
75+
76+
## Detailed Design
77+
78+
```swift
79+
/// Returns the contiguous memory footprint of `T`.
80+
///
81+
/// Does not include any dynamically-allocated or "remote" storage.
82+
/// In particular, `memorySize(X.self)`, when `X` is a class type, is the
83+
/// same regardless of how many stored properties `X` has.
84+
public func memorySize<T>(_: T.Type) -> Int
85+
86+
/// Returns the contiguous memory footprint of `T`.
87+
///
88+
/// Does not include any dynamically-allocated or "remote" storage.
89+
/// In particular, `memorySize(of: a)`, when `a` is a class instance, is the
90+
/// same regardless of how many stored properties `a` has.
91+
public func memorySize<T>(ofValue: T) -> Int
92+
93+
/// Returns the least possible interval between distinct instances of
94+
/// `T` in memory. The result is always positive.
95+
public func memoryInterval<T>(_: T.Type) -> Int
96+
97+
/// Returns the least possible interval between distinct instances of
98+
/// `T` in memory. The result is always positive.
99+
public func memoryInterval<T>(ofValue: T) -> Int
100+
101+
/// Returns the minimum memory alignment of `T`.
102+
public func memoryAlignment<T>(_: T.Type) -> Int
103+
104+
/// Returns the minimum memory alignment of `T`.
105+
public func memoryAlignment<T>(ofValue: T) -> Int
106+
```
107+
108+
### Design Notes
109+
110+
**Labels**: This design omits labels for types. It uses `ofValue` for values, assuming SE-0096 would update to match. This proposal recommends matching SE-0096 regardless of the core team decision.
111+
112+
**Using Autoclosure**: It may make sense to use `@autoclosure` for value variants as the call shouldn't need its arguments evaluated:
113+
114+
```swift
115+
public func memorySize<T>(ofValue _: @autoclosure T -> ()) -> Int
116+
public func memoryInterval<T>(ofValue _: @autoclosure T -> ()) -> Int
117+
public func memoryAlignment<T>(ofValue _: @autoclosure T -> ()) -> Int
118+
```
119+
120+
**Accepting Type Variations**: The core team may choose omit the value variants entirely, replacing just three freestanding functions and removing the other three. In doing so, users must call `type` on passed values. This pattern is already found in standard library code.
121+
122+
Current code:
123+
```swift
124+
let errnoSize = sizeof(errno.dynamicType)
125+
```
126+
127+
Updated code:
128+
```swift
129+
let errnoSize = memorySize(type(ofValue:errno))
130+
```
131+
132+
Pyry Jahkola points out one instance where the `memorySize(type(of: …))` workaround won't work. When the value is an existential, it's illegal to ask for the size of its dynamic type: the result can't be retrieved at compile time:
133+
134+
```swift
135+
// Swift 2.2, 64-bit
136+
let i = 123
137+
print(sizeofValue(i)) //=> 8
138+
let c: CustomStringConvertible = i
139+
print(sizeofValue(c)) //=> 40
140+
print(sizeof(c.dynamicType)) // error: cannot invoke 'sizeof' with an argument list of type '(CustomStringConvertible.Type)'
141+
```
142+
143+
On the other hand, dropping the `ofValue:` variations allows SE-00096 to remain unamended.
144+
145+
146+
## Impact on Existing Code
147+
148+
This proposal requires migration support to rename keywords that use the old
149+
convention to adopt the new convention. This is a simple substitution with
150+
limited impact on existing code that is easily addressed with a fixit.
151+
152+
## Alternatives Considered
153+
154+
Dave Abrahams suggested rather than using global functions, the following design be considered:
155+
156+
```swift
157+
MemoryLayout<T>.size // currently sizeof()
158+
MemoryLayout<T>.spacing // currently strideof()
159+
MemoryLayout<T>.alignment // currently alignof()
160+
```
161+
162+
Dave further recommends that `sizeofValue()`, `strideofValue()`, and `alignofValue()` be completely removed from Swift. Usage numbers from code searches (see above table) support his stance on their value, as instance types can be easily retrieved using `type(of:)`. It is possible to use Dave's design and to retain value functions, as Matthew Johnson and Pyry Jahkola have laid out in on-list discussions.
163+
164+
#### Why not `MemoryLayout`
165+
166+
In the rare times users consume memory layout functionality, using a MemoryLayout type reduces clarity. Consider the following examples, taken from Swift 3.0 stdlib files:
167+
168+
```swift
169+
let errnoSize = sizeof(errno.dynamicType)
170+
return sizeof(UInt) * 8
171+
sendBytes(from: &address, count: sizeof(UInt.self))
172+
_class_getInstancePositiveExtentSize(bufferClass) == sizeof(_HeapObject.self)
173+
bytesPerIndex: sizeof(IndexType)
174+
```
175+
176+
The proposed rewrite for these are:
177+
178+
```swift
179+
let errnoSize = memorySize(ofValue: errno)
180+
return memorySize(UInt.self) * 8
181+
sendBytes(from: &address, count: memorySize(UInt.self))
182+
_class_getInstancePositiveExtentSize(bufferClass) == memorySize(_HeapObject.self)
183+
bytesPerIndex: memorySize(IndexType.self)
184+
```
185+
186+
versus
187+
188+
```swift
189+
let errnoSize = MemoryLayout.init(t: errno).size
190+
return MemoryLayout<UInt>.size * 8
191+
sendBytes(from: &address, count: MemoryLayout<UInt>.size)
192+
_class_getInstancePositiveExtentSize(bufferClass) == MemoryLayout<_HeapObject.self>.size
193+
bytesPerIndex: MemoryLayout<IndexType>.size
194+
```
195+
196+
Swift adheres to a mantra of clarity. In each of the preceding examples, calling a function produces simpler code than using the Memory Layout approach:
197+
198+
* *Early mention of the requested information*: In functions the name (size, spacing/interval, alignment) are stated earlier, supporting reading code in one pass from left to right. Using properties delays recognition and causes the reader longer mental processing times.
199+
* *Simplicity of the function call*: Calls devote the entirety of their name to describing what they do.
200+
* *Prominence of the type constructor*: The eye is drawn to the MemoryLayout pattern. Using full type specification lends calls an importance and verbosity they don't deserve compared to their simpler counterparts.
201+
202+
## Acknowledgements
203+
204+
Thank you, Xiaodi Wu, Matthew Johnson, Pyry Jahkola, Tony Allevato, Joe Groff, Dave Abrahams, and everyone else who contributed to this proposal

0 commit comments

Comments
 (0)